Commit b677925e authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents a8145ff5 8a4c87fe
......@@ -32,7 +32,7 @@ export default {
</script>
<template>
<nav class="ide-activity-bar">
<nav class="ide-activity-bar" data-testid="left-sidebar">
<ul class="list-unstyled">
<li>
<button
......
......@@ -44,7 +44,7 @@ export default {
<nav-dropdown />
<slot name="header"></slot>
</header>
<div class="ide-tree-body h-100">
<div class="ide-tree-body h-100" data-testid="ide-tree-body">
<template v-if="currentTree.tree.length">
<file-tree
v-for="file in currentTree.tree"
......
......@@ -152,6 +152,7 @@ export default {
v-model.trim="entryName"
type="text"
class="form-control"
data-testid="file-name-field"
data-qa-selector="file_name_field"
:placeholder="placeholder"
/>
......
<script>
import { n__ } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import { LIST_INTRO_TEXT, LIST_TITLE_TEXT } from '../constants';
export default {
name: 'PackageTitle',
components: {
TitleArea,
MetadataItem,
},
props: {
packagesCount: {
type: Number,
required: false,
default: null,
},
packageHelpUrl: {
type: String,
required: true,
},
},
computed: {
showPackageCount() {
return Number.isInteger(this.packagesCount);
},
packageAmountText() {
return n__(`%d Package`, `%d Packages`, this.packagesCount);
},
infoMessages() {
return [{ text: LIST_INTRO_TEXT, link: this.packageHelpUrl }];
},
},
i18n: {
LIST_TITLE_TEXT,
},
};
</script>
<template>
<title-area :title="$options.i18n.LIST_TITLE_TEXT" :info-messages="infoMessages">
<template #metadata_amount>
<metadata-item v-if="showPackageCount" icon="package" :text="packageAmountText" />
</template>
</title-area>
</template>
......@@ -3,13 +3,14 @@ import { mapActions, mapState } from 'vuex';
import { GlEmptyState, GlTab, GlTabs, GlLink, GlSprintf } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import createFlash from '~/flash';
import { historyReplaceState } from '~/lib/utils/common_utils';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import PackageFilter from './packages_filter.vue';
import PackageList from './packages_list.vue';
import PackageSort from './packages_sort.vue';
import { PACKAGE_REGISTRY_TABS, DELETE_PACKAGE_SUCCESS_MESSAGE } from '../constants';
import PackagesComingSoon from '../coming_soon/packages_coming_soon.vue';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import { historyReplaceState } from '~/lib/utils/common_utils';
import PackageTitle from './package_title.vue';
export default {
components: {
......@@ -22,6 +23,7 @@ export default {
PackageList,
PackageSort,
PackagesComingSoon,
PackageTitle,
},
computed: {
...mapState({
......@@ -30,6 +32,8 @@ export default {
comingSoon: state => state.config.comingSoon,
filterQuery: state => state.filterQuery,
selectedType: state => state.selectedType,
packageHelpUrl: state => state.config.packageHelpUrl,
packagesCount: state => state.pagination?.total,
}),
tabsToRender() {
return PACKAGE_REGISTRY_TABS;
......@@ -89,39 +93,43 @@ export default {
</script>
<template>
<gl-tabs @input="tabChanged">
<template #tabs-end>
<div
class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end"
>
<package-filter class="mr-1" @filter="requestPackagesList" />
<package-sort @sort:changed="requestPackagesList" />
</div>
</template>
<div>
<package-title :package-help-url="packageHelpUrl" :packages-count="packagesCount" />
<gl-tabs @input="tabChanged">
<template #tabs-end>
<div
class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end"
>
<package-filter class="gl-mr-2" @filter="requestPackagesList" />
<package-sort @sort:changed="requestPackagesList" />
</div>
</template>
<gl-tab v-for="(tab, index) in tabsToRender" :key="index" :title="tab.title">
<package-list @page:changed="onPageChanged" @package:delete="onPackageDeleteRequest">
<template #empty-state>
<gl-empty-state :title="emptyStateTitle(tab)" :svg-path="emptyListIllustration">
<template #description>
<gl-sprintf v-if="filterQuery" :message="$options.i18n.widenFilters" />
<gl-sprintf v-else :message="$options.i18n.noResults">
<template #noPackagesLink="{content}">
<gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
</gl-empty-state>
</template>
</package-list>
</gl-tab>
<gl-tab v-for="(tab, index) in tabsToRender" :key="index" :title="tab.title">
<package-list @page:changed="onPageChanged" @package:delete="onPackageDeleteRequest">
<template #empty-state>
<gl-empty-state :title="emptyStateTitle(tab)" :svg-path="emptyListIllustration">
<template #description>
<gl-sprintf v-if="filterQuery" :message="$options.i18n.widenFilters" />
<gl-sprintf v-else :message="$options.i18n.noResults">
<template #noPackagesLink="{content}">
<gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
</gl-empty-state>
</template>
</package-list>
</gl-tab>
<gl-tab v-if="comingSoon" :title="__('Coming soon')" lazy>
<packages-coming-soon
:illustration="emptyListIllustration"
:project-path="comingSoon.projectPath"
:suggested-contributions-path="comingSoon.suggestedContributions"
/>
</gl-tab>
</gl-tabs>
<gl-tab v-if="comingSoon" :title="__('Coming soon')" lazy>
<packages-coming-soon
:illustration="emptyListIllustration"
:project-path="comingSoon.projectPath"
:suggested-contributions-path="comingSoon.suggestedContributions"
/>
</gl-tab>
</gl-tabs>
</div>
</template>
......@@ -86,3 +86,9 @@ export const PACKAGE_REGISTRY_TABS = [
type: PackageType.PYPI,
},
];
export const LIST_TITLE_TEXT = s__('PackageRegistry|Package Registry');
export const LIST_INTRO_TEXT = s__(
'PackageRegistry|Publish and share packages for a variety of common package managers. %{docLinkStart}More information%{docLinkEnd}',
);
......@@ -53,7 +53,8 @@ module PackagesHelper
page_type: type,
empty_list_help_url: help_page_path('user/packages/package_registry/index'),
empty_list_illustration: image_path('illustrations/no-packages.svg'),
coming_soon_json: packages_coming_soon_data(resource).to_json
coming_soon_json: packages_coming_soon_data(resource).to_json,
package_help_url: help_page_path('user/packages/index')
}
end
end
......@@ -82,8 +82,7 @@ module Ci
schedule_head_pipeline_update if pipeline.persisted?
# If pipeline is not persisted, try to recover IID
pipeline.reset_project_iid unless pipeline.persisted? ||
Feature.disabled?(:ci_pipeline_rewind_iid, project, default_enabled: true)
pipeline.reset_project_iid unless pipeline.persisted?
pipeline
end
......
---
title: Add a title section to the Package Registry UI
merge_request: 42963
author:
type: changed
---
name: ci_pipeline_rewind_iid
introduced_by_url:
rollout_issue_url:
group:
type: development
default_enabled: true
---
name: settings_operations_prometheus_service
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24727
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24296
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560
group: group::health
type: development
......
......@@ -24,7 +24,6 @@ GET /projects/:id/registry/repositories
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) accessible by the authenticated user. |
| `tags` | boolean | no | If the parameter is included as true, each repository will include an array of `"tags"` in the response. |
| `name` | string | no | Returns a list of repositories with a name that matches the value. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29763) in GitLab 13.0). |
| `tags_count` | boolean | no | If the parameter is included as true, each repository will include `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). |
```shell
......@@ -68,7 +67,6 @@ GET /groups/:id/registry/repositories
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) accessible by the authenticated user. |
| `tags` | boolean | no | If the parameter is included as true, each repository will include an array of `"tags"` in the response. |
| `name` | string | no | Returns a list of repositories with a name that matches the value. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29763) in GitLab 13.0). |
| `tags_count` | boolean | no | If the parameter is included as true, each repository will include `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). |
```shell
......
......@@ -332,7 +332,7 @@ applications.
| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From GitLab 11.11, used to set a username to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD`. |
| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From GitLab 11.11, used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. |
| `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm will output debug logs. |
| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. [More details](upgrading_chart.md#ignore-warning-and-continue-deploying) |
| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. For more information, see [Ignore warnings and continue deploying](upgrading_auto_deploy_dependencies.md#ignore-warnings-and-continue-deploying). |
| `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` | From GitLab 12.5, used in combination with [ModSecurity feature flag](../../user/clusters/applications.md#web-application-firewall-modsecurity) to toggle [ModSecurity's `SecRuleEngine`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecRuleEngine) behavior. Defaults to `DetectionOnly`. |
| `BUILDPACK_URL` | Buildpack's full URL. Can point to either [a Git repository URL or a tarball URL](#custom-buildpacks). |
| `CANARY_ENABLED` | From GitLab 11.0, used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
......
......@@ -466,7 +466,7 @@ application runs.
### Upgrade auto-deploy-app Chart
You can upgrade auto-deploy-app chart by following the [upgrade guide](upgrading_chart.md).
You can upgrade the auto-deploy-app chart by following the [upgrade guide](upgrading_auto_deploy_dependencies.md).
### Workers
......
This diff is collapsed.
# Upgrading auto-deploy-app chart for Auto DevOps
---
redirect_to: 'upgrading_auto_deploy_dependencies.md'
---
Auto DevOps provides the auto-deploy-app chart for deploying your application to the
Kubernetes cluster with Helm/Tiller. Major version changes of this chart could have
a significantly different resource architecture, and may not be backwards compatible.
This guide provides instructions on how to upgrade your deployments to use the latest
chart and resource architecture.
## Compatibility
The following table lists the version compatibility between GitLab and [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) (with the [auto-deploy-app chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app)).
| GitLab version | auto-deploy-image version | Notes |
|------------------|---------------------------|--------------------------------------------|
| v10.0 and higher | v0.1.0 and higher | v0 and v1 charts are backwards compatible. |
## Upgrade Guide
The Auto DevOps project must use the unmodified chart managed by GitLab.
[Customized charts](customize.md#custom-helm-chart) are unsupported.
### v1 chart
The v1 chart is backward compatible with the v0 chart, so no configuration changes are needed.
## Troubleshooting
### Major version mismatch warning
If deploying a chart that has a major version that is different from the previous one,
the new chart might not be correctly deployed. This could be due to an architectural
change. If that happens, the deployment job fails with a message similar to:
```plaintext
*************************************************************************************
[WARNING]
Detected a major version difference between the the chart that is currently deploying (auto-deploy-app-v0.7.0), and the previously deployed chart (auto-deploy-app-v1.0.0).
A new major version might not be backward compatible with the current release (production). The deployment could fail or be stuck in an unrecoverable status.
...
```
To clear this error message and resume deployments, you must do one of the following:
- Manually [upgrade the chart version](#upgrade-guide).
- [Use a specific chart version](#use-a-specific-chart-version).
#### Use a specific chart version
To use a specific chart version, you must specify a corresponding version of [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image).
Do this by [customizing the image in your `.gitlab-ci.yml`](customize.md#customizing-gitlab-ciyml).
For example, create the following `.gitlab-ci.yml` file in the project. It configures Auto DevOps
to use [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) version `v0.17.0`
for deployment jobs. It will download the chart from [chart repository](https://charts.gitlab.io/):
```yaml
include:
- template: Auto-DevOps.gitlab-ci.yml
.auto-deploy:
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.17.0"
```
#### Ignore warning and continue deploying
If you are certain that the new chart version is safe to be deployed,
you can add the `AUTO_DEVOPS_FORCE_DEPLOY_V<N>` [environment variable](customize.md#build-and-deployment)
to force the deployment to continue, where `<N>` is the major version.
For example, if you want to deploy the v2.0.0 chart on a deployment that previously
used the v0.17.0 chart, add `AUTO_DEVOPS_FORCE_DEPLOY_V2`.
This document was moved to [another location](upgrading_auto_deploy_dependencies.md).
......@@ -2,7 +2,6 @@
import produce from 'immer';
import { GlAlert, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
import { __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import VulnerabilityList from './vulnerability_list.vue';
import vulnerabilitiesQuery from '../graphql/project_vulnerabilities.graphql';
import securityScannersQuery from '../graphql/project_security_scanners.graphql';
......@@ -17,7 +16,6 @@ export default {
GlIntersectionObserver,
VulnerabilityList,
},
mixins: [glFeatureFlagsMixin()],
props: {
projectFullPath: {
type: String,
......@@ -78,9 +76,6 @@ export default {
pipelineRun: pipelineRun.map(translateScannerName),
};
},
skip() {
return !this.glFeatures.scannerAlerts;
},
},
},
computed: {
......
......@@ -144,13 +144,13 @@ export default {
</div>
<div class="ci-table" role="grid">
<div
class="gl-responsive-table-row table-row-header bg-gray-light pl-2 border-top mt-3 lh-100"
class="gl-responsive-table-row table-row-header gl-pl-3 gl-border-t-solid gl-border-t-1 gl-border-gray-100 gl-mt-5 gl-line-height-normal gl-text-black-normal gl-font-base"
role="row"
>
<div class="table-section section-70 font-weight-bold" role="columnheader">
<div class="table-section section-70 gl-font-weight-bold" role="columnheader">
{{ __('Project') }}
</div>
<div class="table-section section-30 font-weight-bold" role="columnheader">
<div class="table-section section-30 gl-font-weight-bold" role="columnheader">
{{ __('Usage') }}
</div>
</div>
......
<script>
import { GlButton, GlLink, GlIcon } from '@gitlab/ui';
import { GlLink, GlIcon } from '@gitlab/ui';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { numberToHumanSize, isOdd } from '~/lib/utils/number_utils';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { s__ } from '~/locale';
import StorageRow from './storage_row.vue';
export default {
components: {
GlIcon,
GlButton,
GlLink,
ProjectAvatar,
StorageRow,
......@@ -29,7 +29,7 @@ export default {
const { name, id, avatarUrl, webUrl } = this.project;
return {
name,
id: Number(id),
id: Number(getIdFromGraphQLId(id)),
avatar_url: avatarUrl,
path: webUrl,
};
......@@ -54,7 +54,13 @@ export default {
},
},
methods: {
toggleProject() {
toggleProject(e) {
const NO_EXPAND_CLS = 'js-project-link';
const targetClasses = e.target.classList;
if (targetClasses.contains(NO_EXPAND_CLS)) {
return;
}
this.isOpen = !this.isOpen;
},
getFormattedName(name) {
......@@ -83,27 +89,42 @@ export default {
</script>
<template>
<div>
<div class="gl-responsive-table-row border-bottom" role="row">
<div class="table-section section-wrap section-70 text-truncate" role="gridcell">
<div class="table-mobile-header font-weight-bold" role="rowheader">{{ __('Project') }}</div>
<div class="table-mobile-content">
<gl-button
class="btn-transparent float-left p-0 mr-2"
:aria-label="__('Toggle project')"
category="tertiary"
@click="toggleProject"
<div
class="gl-responsive-table-row gl-border-solid gl-border-b-1 gl-pt-3 gl-pb-3 gl-border-b-gray-100 gl-hover-bg-blue-50 gl-hover-border-blue-200 gl-hover-cursor-pointer"
role="row"
data-testid="projectTableRow"
@click="toggleProject"
>
<div
class="table-section gl-white-space-normal! gl-flex-sm-wrap section-70 gl-text-truncate"
role="gridcell"
>
<div class="table-mobile-header gl-font-weight-bold" role="rowheader">
{{ __('Project') }}
</div>
<div class="table-mobile-content gl-display-flex gl-align-items-center">
<div class="gl-display-flex gl-mr-3 gl-align-items-center">
<gl-icon :size="10" :name="iconName" class="gl-mr-2" />
<gl-icon name="bookmark" />
</div>
<div>
<project-avatar :project="projectAvatar" :size="32" />
</div>
<gl-link
:href="project.webUrl"
class="js-project-link gl-font-weight-bold gl-text-gray-900!"
>{{ name }}</gl-link
>
<gl-icon :name="iconName" class="folder-icon" />
</gl-button>
<project-avatar :project="projectAvatar" :size="20" />
<gl-link :href="project.webUrl" class="font-weight-bold">{{ name }}</gl-link>
</div>
</div>
<div class="table-section section-wrap section-30 text-truncate" role="gridcell">
<div class="table-mobile-header font-weight-bold" role="rowheader">{{ __('Usage') }}</div>
<div class="table-mobile-content">{{ storageSize }}</div>
<div
class="table-section gl-white-space-normal! gl-flex-sm-wrap section-30 gl-text-truncate"
role="gridcell"
>
<div class="table-mobile-header gl-font-weight-bold" role="rowheader">
{{ __('Usage') }}
</div>
<div class="table-mobile-content gl-text-gray-900">{{ storageSize }}</div>
</div>
</div>
......@@ -113,7 +134,7 @@ export default {
:key="index"
:name="getFormattedName(statisticsName)"
:value="getValue(value)"
:class="{ 'bg-gray-light': isOdd(index) }"
:class="{ 'gl-bg-gray-10': isOdd(index) }"
/>
</template>
</div>
......
......@@ -9,7 +9,6 @@ module Projects
before_action only: [:index] do
push_frontend_feature_flag(:hide_dismissed_vulnerabilities)
push_frontend_feature_flag(:scanner_alerts, default_enabled: true)
end
end
end
......
---
title: Improve projects table in Usage quotas page
merge_request: 43080
author:
type: changed
---
name: scanner_alerts
introduced_by_url:
rollout_issue_url:
group:
type: development
default_enabled: true
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import Project from 'ee/storage_counter/components/project.vue';
import StorageRow from 'ee/storage_counter/components/storage_row.vue';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
......@@ -32,6 +32,9 @@ function factory(project) {
});
}
const findTableRow = () => wrapper.find('[data-testid="projectTableRow"]');
const findStorageRow = () => wrapper.find(StorageRow);
describe('Storage Counter project component', () => {
beforeEach(() => {
factory(data);
......@@ -52,15 +55,18 @@ describe('Storage Counter project component', () => {
describe('toggle row', () => {
describe('on click', () => {
it('toggles isOpen', () => {
expect(wrapper.vm.isOpen).toEqual(false);
wrapper.find(GlButton).vm.$emit('click');
expect(findStorageRow().exists()).toBe(false);
expect(wrapper.vm.isOpen).toEqual(true);
findTableRow().trigger('click');
wrapper.find(GlButton).vm.$emit('click');
wrapper.vm.$nextTick(() => {
expect(findStorageRow().exists()).toBe(true);
findTableRow().trigger('click');
expect(wrapper.vm.isOpen).toEqual(false);
wrapper.vm.$nextTick(() => {
expect(findStorageRow().exists()).toBe(false);
});
});
});
});
});
......
.auto-deploy:
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.3"
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0-beta.2"
dependencies: []
review:
......@@ -91,7 +91,7 @@ canary:
- auto-deploy ensure_namespace
- auto-deploy initialize_tiller
- auto-deploy create_secret
- auto-deploy deploy canary
- auto-deploy deploy canary 50
environment:
name: production
url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
......@@ -114,7 +114,6 @@ canary:
- auto-deploy create_secret
- auto-deploy deploy
- auto-deploy delete canary
- auto-deploy delete rollout
- auto-deploy persist_environment_url
environment:
name: production
......@@ -163,9 +162,7 @@ production_manual:
- auto-deploy ensure_namespace
- auto-deploy initialize_tiller
- auto-deploy create_secret
- auto-deploy deploy rollout $ROLLOUT_PERCENTAGE
- auto-deploy scale stable $((100-ROLLOUT_PERCENTAGE))
- auto-deploy delete canary
- auto-deploy deploy canary $ROLLOUT_PERCENTAGE
- auto-deploy persist_environment_url
environment:
name: production
......
......@@ -137,6 +137,7 @@ module Gitlab
# TODO this class is meant to replace Handler when the feature flag
# upload_middleware_jwt_params_handler is removed
# See https://gitlab.com/gitlab-org/gitlab/-/issues/233895#roll-out-steps
class HandlerForJWTParams < Handler
def with_open_files
@rewritten_fields.keys.each do |field|
......
......@@ -42,6 +42,9 @@ class UploadedFile
@remote_id = remote_id
end
# TODO this function is meant to replace .from_params when the feature flag
# upload_middleware_jwt_params_handler is removed
# See https://gitlab.com/gitlab-org/gitlab/-/issues/233895#roll-out-steps
def self.from_params_without_field(params, upload_paths)
path = params['path']
remote_id = params['remote_id']
......@@ -68,6 +71,10 @@ class UploadedFile
)
end
# Deprecated. Don't use it.
# .from_params_without_field will replace this one
# See .from_params_without_field and
# https://gitlab.com/gitlab-org/gitlab/-/issues/233895#roll-out-steps
def self.from_params(params, field, upload_paths, path_override = nil)
path = path_override || params["#{field}.path"]
remote_id = params["#{field}.remote_id"]
......
......@@ -84,6 +84,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
msgstr[1] ""
msgid "%d Scanned URL"
msgid_plural "%d Scanned URLs"
msgstr[0] ""
......@@ -18185,12 +18190,18 @@ msgstr ""
msgid "PackageRegistry|NuGet Command"
msgstr ""
msgid "PackageRegistry|Package Registry"
msgstr ""
msgid "PackageRegistry|Pip Command"
msgstr ""
msgid "PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}"
msgstr ""
msgid "PackageRegistry|Publish and share packages for a variety of common package managers. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
msgid "PackageRegistry|Published to the %{project} Package Registry %{datetime}"
msgstr ""
......@@ -26911,9 +26922,6 @@ msgstr ""
msgid "Toggle navigation"
msgstr ""
msgid "Toggle project"
msgstr ""
msgid "Toggle sidebar"
msgstr ""
......
import { findByText } from '@testing-library/dom';
export const waitForText = async (text, container = document) => findByText(container, text);
......@@ -36,6 +36,7 @@ describe('packages_list_app', () => {
resourceId: 'project_id',
emptyListIllustration: 'helpSvg',
emptyListHelpUrl,
packageHelpUrl: 'foo',
},
filterQuery,
},
......
import { shallowMount } from '@vue/test-utils';
import PackageTitle from '~/packages/list/components/package_title.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import { LIST_INTRO_TEXT, LIST_TITLE_TEXT } from '~/packages/list//constants';
describe('PackageTitle', () => {
let wrapper;
let store;
const findTitleArea = () => wrapper.find(TitleArea);
const findMetadataItem = () => wrapper.find(MetadataItem);
const mountComponent = (propsData = { packageHelpUrl: 'foo' }) => {
wrapper = shallowMount(PackageTitle, {
store,
propsData,
stubs: {
TitleArea,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('title area', () => {
it('exists', () => {
mountComponent();
expect(findTitleArea().exists()).toBe(true);
});
it('has the correct props', () => {
mountComponent();
expect(findTitleArea().props()).toMatchObject({
title: LIST_TITLE_TEXT,
infoMessages: [{ text: LIST_INTRO_TEXT, link: 'foo' }],
});
});
});
describe.each`
packagesCount | exist | text
${null} | ${false} | ${''}
${undefined} | ${false} | ${''}
${0} | ${true} | ${'0 Packages'}
${1} | ${true} | ${'1 Package'}
${2} | ${true} | ${'2 Packages'}
`('when packagesCount is $packagesCount metadata item', ({ packagesCount, exist, text }) => {
beforeEach(() => {
mountComponent({ packagesCount, packageHelpUrl: 'foo' });
});
it(`is ${exist} that it exists`, () => {
expect(findMetadataItem().exists()).toBe(exist);
});
if (exist) {
it('has the correct props', () => {
expect(findMetadataItem().props()).toMatchObject({
icon: 'package',
text,
});
});
}
});
});
......@@ -4,3 +4,5 @@ settings:
import/resolver:
jest:
jestConfigFile: 'jest.config.integration.js'
globals:
mockServer: false
import { findAllByText, fireEvent, getByLabelText, screen } from '@testing-library/dom';
const isFileRowOpen = row => row.matches('.is-open');
const getLeftSidebar = () => screen.getByTestId('left-sidebar');
const clickOnLeftSidebarTab = name => {
const sidebar = getLeftSidebar();
const button = getByLabelText(sidebar, name);
button.click();
};
const findMonacoEditor = () =>
screen.findByLabelText(/Editor content;/).then(x => x.closest('.monaco-editor'));
const findAndSetEditorValue = async value => {
const editor = await findMonacoEditor();
const uri = editor.getAttribute('data-uri');
window.monaco.editor.getModel(uri).setValue(value);
};
const findTreeBody = () => screen.findByTestId('ide-tree-body', {}, { timeout: 5000 });
const findFileRowContainer = (row = null) =>
row ? Promise.resolve(row.parentElement) : findTreeBody();
const findFileChild = async (row, name, index = 0) => {
const container = await findFileRowContainer(row);
const children = await findAllByText(container, name, { selector: '.file-row-name' });
return children.map(x => x.closest('.file-row')).find(x => x.dataset.level === index.toString());
};
const openFileRow = row => {
if (!row || isFileRowOpen(row)) {
return;
}
row.click();
};
const findAndTraverseToPath = async (path, index = 0, row = null) => {
if (!path) {
return row;
}
const [name, ...restOfPath] = path.split('/');
openFileRow(row);
const child = await findFileChild(row, name, index);
return findAndTraverseToPath(restOfPath.join('/'), index + 1, child);
};
const clickFileRowAction = (row, name) => {
fireEvent.mouseOver(row);
const dropdownButton = getByLabelText(row, 'Create new file or directory');
dropdownButton.click();
const dropdownAction = getByLabelText(dropdownButton.parentNode, name);
dropdownAction.click();
};
const findAndSetFileName = async value => {
const nameField = await screen.findByTestId('file-name-field');
fireEvent.input(nameField, { target: { value } });
const createButton = screen.getByText('Create file');
createButton.click();
};
export const createFile = async (path, content) => {
const parentPath = path
.split('/')
.slice(0, -1)
.join('/');
const parentRow = await findAndTraverseToPath(parentPath);
clickFileRowAction(parentRow, 'New file');
await findAndSetFileName(path);
await findAndSetEditorValue(content);
};
export const deleteFile = async path => {
const row = await findAndTraverseToPath(path);
clickFileRowAction(row, 'Delete');
};
export const commit = async () => {
clickOnLeftSidebarTab('Commit');
screen.getByTestId('begin-commit-button').click();
await screen.findByLabelText(/Commit to .+ branch/).then(x => x.click());
screen.getByText('Commit').click();
};
/**
* WARNING: WIP
*
* Please do not copy from this spec or use it as an example for anything.
*
* This is in place to iteratively set up the frontend integration testing environment
* and will be improved upon in a later iteration.
*
* See https://gitlab.com/gitlab-org/gitlab/-/issues/208800 for more information.
*/
import { TEST_HOST } from 'helpers/test_constants';
import { waitForText } from 'helpers/wait_for_text';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
import { createCommitId } from 'test_helpers/factories/commit_id';
import { initIde } from '~/ide';
import extendStore from '~/ide/stores/extend';
import * as ideHelper from './ide_helper';
const TEST_DATASET = {
emptyStateSvgPath: '/test/empty_state.svg',
......@@ -59,4 +52,38 @@ describe('WebIDE', () => {
expect(root).toMatchSnapshot();
});
it('user commits changes', async () => {
createComponent();
await ideHelper.createFile('foo/bar/test.txt', 'Lorem ipsum dolar sit');
await ideHelper.deleteFile('foo/bar/.gitkeep');
await ideHelper.commit();
const commitId = createCommitId(1);
const commitShortId = commitId.slice(0, 8);
await waitForText('All changes are committed');
await waitForText(commitShortId);
expect(mockServer.db.branches.findBy({ name: 'master' }).commit).toMatchObject({
short_id: commitShortId,
id: commitId,
message: 'Update foo/bar/test.txt\nDeleted foo/bar/.gitkeep',
__actions: [
{
action: 'create',
content: 'Lorem ipsum dolar sit\n',
encoding: 'text',
file_path: 'foo/bar/test.txt',
last_commit_id: '',
},
{
action: 'delete',
encoding: 'text',
file_path: 'foo/bar/.gitkeep',
},
],
});
});
});
import { createMockServer } from '../mock_server';
beforeEach(() => {
if (global.mockServer) {
global.mockServer.shutdown();
}
const server = createMockServer();
server.logging = false;
global.mockServer = server;
});
afterEach(() => {
global.mockServer.shutdown();
global.mockServer = null;
});
......@@ -731,30 +731,11 @@ RSpec.describe Ci::CreatePipelineService do
.and_call_original
end
context 'when ci_pipeline_rewind_iid is enabled' do
before do
stub_feature_flags(ci_pipeline_rewind_iid: true)
end
it 'rewinds iid' do
result = execute_service
expect(result).not_to be_persisted
expect(internal_id.last_value).to eq(0)
end
end
context 'when ci_pipeline_rewind_iid is disabled' do
before do
stub_feature_flags(ci_pipeline_rewind_iid: false)
end
it 'does not rewind iid' do
result = execute_service
it 'rewinds iid' do
result = execute_service
expect(result).not_to be_persisted
expect(internal_id.last_value).to eq(1)
end
expect(result).not_to be_persisted
expect(internal_id.last_value).to eq(0)
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment