Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a8c410f8a1
commit
b8915a9ca9
2
Gemfile
2
Gemfile
|
|
@ -510,7 +510,7 @@ gem 'gitaly', '~> 15.9.0-rc3'
|
|||
# KAS GRPC protocol definitions
|
||||
gem 'kas-grpc', '~> 0.1.0'
|
||||
|
||||
gem 'grpc', '~> 1.42.0'
|
||||
gem 'grpc', '~> 1.55.0'
|
||||
|
||||
gem 'google-protobuf', '~> 3.23', '>= 3.23.1'
|
||||
|
||||
|
|
|
|||
|
|
@ -269,12 +269,13 @@
|
|||
{"name":"graphql","version":"1.13.12","platform":"ruby","checksum":"1d82666cf201193a8d0cb54cea38576b820418db4869b549f61a35f3a2d97ac3"},
|
||||
{"name":"graphql-client","version":"0.17.0","platform":"ruby","checksum":"5aaf02ce8f2dbc8e3ba05a7eaeb3ad9336762c4424c6093f4438fbb9490eeb5d"},
|
||||
{"name":"graphql-docs","version":"2.1.0","platform":"ruby","checksum":"7eb82402f8fda455104b2b60364e9ada145d79d3121a8f915790d49da38bb576"},
|
||||
{"name":"grpc","version":"1.42.0","platform":"ruby","checksum":"b3d2649e67c6a636544996843d9ec191699c54c1aca797dbfea4dff36c14584a"},
|
||||
{"name":"grpc","version":"1.42.0","platform":"x64-mingw32","checksum":"6aac1b6576134b0a83e000b1269f60d502eb24aee96c64e2658c3f24f8e32ac0"},
|
||||
{"name":"grpc","version":"1.42.0","platform":"x86-linux","checksum":"4aa50538aa929f1f3bcefb11c65ee1a1606b5aef838ea4d4e93c100b5f4263a5"},
|
||||
{"name":"grpc","version":"1.42.0","platform":"x86-mingw32","checksum":"eeb2a9381bea43fafe879b6ddaa011351a44d0894d48bdc965a07bcb67c6eb56"},
|
||||
{"name":"grpc","version":"1.42.0","platform":"x86_64-darwin","checksum":"20fa202d46d8a055628260622e98fb6439529fbac283f0552af620b909f78535"},
|
||||
{"name":"grpc","version":"1.42.0","platform":"x86_64-linux","checksum":"92e2ceb2aca335d5755163dd8030082091d5b0e63c117b1ca07051b66c53eb2e"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"ruby","checksum":"529332f8e5e98f5b138afd5c4a9c7bdc9e247f4c10c84c1adbf1a114eba161ae"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"x64-mingw-ucrt","checksum":"6b5c7b7358476469c5ecb46f35e1eff6983efc9395d9db8db0a2eb4207c82ffb"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"x64-mingw32","checksum":"73755c256fc0fe5361a979cd609414ebdaa5862f5821fba20ea31110f1d87405"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"x86-linux","checksum":"37c20569a17b1cff91155f193b0df41eb42fd0aed9051fa91ccca273a259e393"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"x86-mingw32","checksum":"6b4144b5af8086b46b2e62b5fbda50fc19105a4efefafaca63e15b0384c42274"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"x86_64-darwin","checksum":"d7f57eb84811d7ea2a9464ec88d9296a92801f643a4d7cf76cf4896edf12a25c"},
|
||||
{"name":"grpc","version":"1.55.0","platform":"x86_64-linux","checksum":"4ee73555759774db22ba23ff79c332cce7ae08b0ba4d4b33ab4747e83e0a8518"},
|
||||
{"name":"gssapi","version":"1.3.1","platform":"ruby","checksum":"c51cf30842ee39bd93ce7fc33e20405ff8a04cda9dec6092071b61258284aee1"},
|
||||
{"name":"guard","version":"2.16.2","platform":"ruby","checksum":"71ba7abaddecc8be91ab77bbaf78f767246603652ebbc7b976fda497ebdc8fbb"},
|
||||
{"name":"guard-compat","version":"1.2.1","platform":"ruby","checksum":"3ad21ab0070107f92edfd82610b5cdc2fb8e368851e72362ada9703443d646fe"},
|
||||
|
|
|
|||
|
|
@ -750,8 +750,8 @@ GEM
|
|||
graphql (~> 1.12)
|
||||
html-pipeline (~> 2.9)
|
||||
sass (~> 3.4)
|
||||
grpc (1.42.0)
|
||||
google-protobuf (~> 3.18)
|
||||
grpc (1.55.0)
|
||||
google-protobuf (~> 3.23)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
gssapi (1.3.1)
|
||||
ffi (>= 1.0.1)
|
||||
|
|
@ -1777,7 +1777,7 @@ DEPENDENCIES
|
|||
graphlyte (~> 1.0.0)
|
||||
graphql (~> 1.13.12)
|
||||
graphql-docs (~> 2.1.0)
|
||||
grpc (~> 1.42.0)
|
||||
grpc (~> 1.55.0)
|
||||
gssapi (~> 1.3.1)
|
||||
guard-rspec
|
||||
haml_lint (~> 0.40.0)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@
|
|||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import createEnvironment from '../graphql/mutations/create_environment.mutation.graphql';
|
||||
import EnvironmentForm from './environment_form.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EnvironmentForm,
|
||||
},
|
||||
inject: ['projectEnvironmentsPath'],
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
inject: ['projectEnvironmentsPath', 'projectPath'],
|
||||
data() {
|
||||
return {
|
||||
environment: {
|
||||
|
|
@ -23,6 +26,45 @@ export default {
|
|||
this.environment = env;
|
||||
},
|
||||
onSubmit() {
|
||||
if (this.glFeatures?.environmentSettingsToGraphql) {
|
||||
this.createWithGraphql();
|
||||
} else {
|
||||
this.createWithAxios();
|
||||
}
|
||||
},
|
||||
async createWithGraphql() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: createEnvironment,
|
||||
variables: {
|
||||
input: {
|
||||
name: this.environment.name,
|
||||
externalUrl: this.environment.externalUrl,
|
||||
projectPath: this.projectPath,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { errors } = data.environmentCreate;
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(errors[0]?.message ?? errors[0]);
|
||||
}
|
||||
|
||||
const { path } = data.environmentCreate.environment;
|
||||
|
||||
if (path) {
|
||||
visitUrl(path);
|
||||
}
|
||||
} catch (error) {
|
||||
const { message } = error;
|
||||
createAlert({ message });
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
createWithAxios() {
|
||||
this.loading = true;
|
||||
axios
|
||||
.post(this.projectEnvironmentsPath, {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
mutation createEnvironment($input: EnvironmentCreateInput!) {
|
||||
environmentCreate(input: $input) {
|
||||
environment {
|
||||
id
|
||||
path
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import NewEnvironment from './components/new_environment.vue';
|
||||
import { apolloProvider } from './graphql/client';
|
||||
|
||||
export default (el) =>
|
||||
new Vue({
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export default (el) => {
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { projectEnvironmentsPath, projectPath } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
provide: { projectEnvironmentsPath: el.dataset.projectEnvironmentsPath },
|
||||
apolloProvider: apolloProvider(),
|
||||
provide: { projectEnvironmentsPath, projectPath },
|
||||
render(h) {
|
||||
return h(NewEnvironment);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -180,7 +180,6 @@ export default {
|
|||
},
|
||||
},
|
||||
epicLink: 'https://gitlab.com/gitlab-org/gitlab/-/issues/353639',
|
||||
openBetaLink: 'https://about.gitlab.com/handbook/product/gitlab-the-product/#open-beta',
|
||||
featureFlagLink: helpPagePath('operations/error_tracking'),
|
||||
created() {
|
||||
if (this.errorTrackingEnabled) {
|
||||
|
|
@ -476,10 +475,6 @@ export default {
|
|||
__('How do I get started?')
|
||||
}}</gl-link>
|
||||
</div>
|
||||
<div class="gl-mt-3">
|
||||
<span>{{ __('Error tracking is currently in') }}</span>
|
||||
<gl-link target="_blank" :href="$options.openBetaLink">{{ __('Open Beta.') }}</gl-link>
|
||||
</div>
|
||||
</template>
|
||||
</gl-empty-state>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ query getUserSnippets(
|
|||
name
|
||||
}
|
||||
}
|
||||
commenters {
|
||||
notes {
|
||||
nodes {
|
||||
id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,27 @@
|
|||
<script>
|
||||
import { GlAvatar, GlLink, GlSprintf, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { s__, sprintf, n__ } from '~/locale';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { SNIPPET_VISIBILITY } from '~/snippets/constants';
|
||||
|
||||
export default {
|
||||
name: 'SnippetRow',
|
||||
i18n: {
|
||||
snippetInfo: s__('UserProfile|%{id} · created %{created} by %{author}'),
|
||||
updatedInfo: s__('UserProfile|updated %{updated}'),
|
||||
blobTooltip: s__('UserProfile|%{count} %{file}'),
|
||||
},
|
||||
components: {
|
||||
GlAvatar,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
GlIcon,
|
||||
TimeAgo,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
snippet: {
|
||||
type: Object,
|
||||
|
|
@ -11,11 +32,88 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
formattedId() {
|
||||
return `$${getIdFromGraphQLId(this.snippet.id)}`;
|
||||
},
|
||||
profilePath() {
|
||||
return `${gon.relative_url_root || ''}/${this.userInfo.username}`;
|
||||
},
|
||||
blobCount() {
|
||||
return this.snippet.blobs?.nodes?.length || 0;
|
||||
},
|
||||
commentsCount() {
|
||||
return this.snippet.notes?.nodes?.length || 0;
|
||||
},
|
||||
visibilityIcon() {
|
||||
return SNIPPET_VISIBILITY[this.snippet.visibilityLevel]?.icon;
|
||||
},
|
||||
blobTooltip() {
|
||||
return sprintf(this.$options.i18n.blobTooltip, {
|
||||
count: this.blobCount,
|
||||
file: n__('file', 'files', this.blobCount),
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ snippet.title }}
|
||||
<div class="gl-display-flex gl-align-items-center gl-py-5">
|
||||
<gl-avatar :size="48" :src="userInfo.avatarUrl" class="gl-mr-3" />
|
||||
<div class="gl-display-flex gl-flex-direction-column gl-align-items-flex-start">
|
||||
<gl-link
|
||||
data-testid="snippet-url"
|
||||
:href="snippet.webUrl"
|
||||
class="gl-text-gray-900 gl-font-weight-bold gl-mb-2"
|
||||
>{{ snippet.title }}</gl-link
|
||||
>
|
||||
<span class="gl-text-gray-500">
|
||||
<gl-sprintf :message="$options.i18n.snippetInfo">
|
||||
<template #id>
|
||||
<span data-testid="snippet-id">{{ formattedId }}</span>
|
||||
</template>
|
||||
<template #created>
|
||||
<time-ago data-testid="snippet-created-at" :time="snippet.createdAt" />
|
||||
</template>
|
||||
<template #author>
|
||||
<gl-link data-testid="snippet-author" :href="profilePath" class="gl-text-gray-900">{{
|
||||
userInfo.name
|
||||
}}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</div>
|
||||
<div class="gl-ml-auto gl-display-flex gl-flex-direction-column gl-align-items-flex-end">
|
||||
<div class="gl-display-flex gl-align-items-center gl-mb-2">
|
||||
<span
|
||||
v-gl-tooltip
|
||||
data-testid="snippet-blob"
|
||||
:title="blobTooltip"
|
||||
class="gl-mr-4"
|
||||
:class="{ 'gl-opacity-5': blobCount === 0 }"
|
||||
>
|
||||
<gl-icon name="documents" />
|
||||
<span>{{ blobCount }}</span>
|
||||
</span>
|
||||
<gl-link
|
||||
data-testid="snippet-comments"
|
||||
:href="`${snippet.webUrl}#notes`"
|
||||
class="gl-mr-4 gl-text-gray-900"
|
||||
:class="{ 'gl-opacity-5': commentsCount === 0 }"
|
||||
>
|
||||
<gl-icon name="comments" />
|
||||
<span>{{ commentsCount }}</span>
|
||||
</gl-link>
|
||||
<gl-icon data-testid="snippet-visibility" :name="visibilityIcon" />
|
||||
</div>
|
||||
<span class="gl-text-gray-500">
|
||||
<gl-sprintf :message="$options.i18n.updatedInfo">
|
||||
<template #updated>
|
||||
<time-ago data-testid="snippet-updated-at" :time="snippet.updatedAt" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import getStateKey from 'ee_else_ce/vue_merge_request_widget/stores/get_state_key';
|
||||
import { badgeState } from '~/issuable/components/status_box.vue';
|
||||
import { STATUS_CLOSED, STATUS_MERGED, STATUS_OPEN } from '~/issues/constants';
|
||||
import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
|
||||
import { formatDate, getTimeago, timeagoLanguageCode } from '~/lib/utils/datetime_utility';
|
||||
import { machine } from '~/lib/utils/finite_state_machine';
|
||||
import {
|
||||
MTWPS_MERGE_STRATEGY,
|
||||
|
|
@ -341,7 +341,7 @@ export default class MergeRequestStore {
|
|||
return '';
|
||||
}
|
||||
|
||||
return format(date);
|
||||
return format(date, timeagoLanguageCode);
|
||||
}
|
||||
|
||||
static getPreferredAutoMergeStrategy(availableAutoMergeStrategies) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ module Integrations
|
|||
:app_store_key_id,
|
||||
:app_store_private_key,
|
||||
:app_store_private_key_file_name,
|
||||
:app_store_protected_refs,
|
||||
:active,
|
||||
:alert_events,
|
||||
:api_key,
|
||||
|
|
|
|||
|
|
@ -650,9 +650,8 @@ module Ci
|
|||
|
||||
def apple_app_store_variables
|
||||
return [] unless apple_app_store_integration.try(:activated?)
|
||||
return [] unless pipeline.protected_ref?
|
||||
|
||||
Gitlab::Ci::Variables::Collection.new(apple_app_store_integration.ci_variables)
|
||||
Gitlab::Ci::Variables::Collection.new(apple_app_store_integration.ci_variables(protected_ref: pipeline.protected_ref?))
|
||||
end
|
||||
|
||||
def google_play_variables
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ module Integrations
|
|||
validates :app_store_key_id, presence: true, format: { with: KEY_ID_REGEX }
|
||||
validates :app_store_private_key, presence: true, certificate_key: true
|
||||
validates :app_store_private_key_file_name, presence: true
|
||||
validates :app_store_protected_refs, inclusion: [true, false]
|
||||
end
|
||||
|
||||
field :app_store_issuer_id,
|
||||
|
|
@ -30,6 +31,12 @@ module Integrations
|
|||
field :app_store_private_key_file_name, section: SECTION_TYPE_CONNECTION
|
||||
field :app_store_private_key, api_only: true
|
||||
|
||||
field :app_store_protected_refs,
|
||||
type: 'checkbox',
|
||||
section: SECTION_TYPE_CONFIGURATION,
|
||||
title: -> { s_('AppleAppStore|Protected branches and tags only') },
|
||||
checkbox_label: -> { s_('AppleAppStore|Only set variables on protected branches and tags') }
|
||||
|
||||
def title
|
||||
'Apple App Store Connect'
|
||||
end
|
||||
|
|
@ -85,8 +92,9 @@ module Integrations
|
|||
end
|
||||
end
|
||||
|
||||
def ci_variables
|
||||
def ci_variables(protected_ref:)
|
||||
return [] unless activated?
|
||||
return [] if app_store_protected_refs && !protected_ref
|
||||
|
||||
[
|
||||
{ key: 'APP_STORE_CONNECT_API_KEY_ISSUER_ID', value: app_store_issuer_id, masked: true, public: false },
|
||||
|
|
@ -98,6 +106,11 @@ module Integrations
|
|||
]
|
||||
end
|
||||
|
||||
def initialize_properties
|
||||
super
|
||||
self.app_store_protected_refs = true if app_store_protected_refs.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def client
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
- page_title s_("Environments|New Environment")
|
||||
- add_page_specific_style 'page_bundles/environments'
|
||||
|
||||
#js-new-environment{ data: { project_environments_path: project_environments_path(@project) } }
|
||||
#js-new-environment{ data: { project_environments_path: project_environments_path(@project), project_path: @project.full_path, } }
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ GitLab supports enabling the Apple App Store integration at the project level. C
|
|||
- **Issuer ID**: The Apple App Store Connect issuer ID.
|
||||
- **Key ID**: The key ID of the generated private key.
|
||||
- **Private Key**: The generated private key. You can download this key only once.
|
||||
- **Protected branches and tags only**: Enable to only set variables on protected branches and tags.
|
||||
|
||||
1. Select **Save changes**.
|
||||
|
||||
|
|
|
|||
|
|
@ -197,6 +197,12 @@ module API
|
|||
name: :app_store_private_key_file_name,
|
||||
type: String,
|
||||
desc: 'The Apple App Store Connect Private Key File Name'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :app_store_protected_refs,
|
||||
type: Boolean,
|
||||
desc: 'Only enable for protected refs'
|
||||
}
|
||||
],
|
||||
'asana' => [
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ module Gitlab
|
|||
module GrpcErrorProcessor
|
||||
extend Gitlab::ErrorTracking::Processor::Concerns::ProcessesExceptions
|
||||
|
||||
DEBUG_ERROR_STRING_REGEX = RE2('(.*) debug_error_string:(.*)')
|
||||
# Braces added by gRPC Ruby code: https://github.com/grpc/grpc/blob/0e38b075ffff72ab2ad5326e3f60ba6dcc234f46/src/ruby/lib/grpc/errors.rb#L46
|
||||
DEBUG_ERROR_STRING_REGEX = RE2('(.*) debug_error_string:\{(.*)\}')
|
||||
|
||||
class << self
|
||||
def call(event)
|
||||
|
|
|
|||
|
|
@ -5409,6 +5409,12 @@ msgstr ""
|
|||
msgid "AppleAppStore|Leave empty to use your current Private Key."
|
||||
msgstr ""
|
||||
|
||||
msgid "AppleAppStore|Only set variables on protected branches and tags"
|
||||
msgstr ""
|
||||
|
||||
msgid "AppleAppStore|Protected branches and tags only"
|
||||
msgstr ""
|
||||
|
||||
msgid "AppleAppStore|The Apple App Store Connect Issuer ID."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17625,9 +17631,6 @@ msgstr ""
|
|||
msgid "Error tracking"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error tracking is currently in"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error updating %{issuableType}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -31624,9 +31627,6 @@ msgstr ""
|
|||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open Beta."
|
||||
msgstr ""
|
||||
|
||||
msgid "Open Selection"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -49193,6 +49193,12 @@ msgstr ""
|
|||
msgid "UserProfiles|No snippets found."
|
||||
msgstr ""
|
||||
|
||||
msgid "UserProfile|%{count} %{file}"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserProfile|%{id} · created %{created} by %{author}"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserProfile|Activity"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -49334,6 +49340,9 @@ msgstr ""
|
|||
msgid "UserProfile|made a private contribution"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserProfile|updated %{updated}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -297,6 +297,7 @@ FactoryBot.define do
|
|||
app_store_key_id { 'ABC1' }
|
||||
app_store_private_key_file_name { 'auth_key.p8' }
|
||||
app_store_private_key { File.read('spec/fixtures/auth_key.p8') }
|
||||
app_store_protected_refs { true }
|
||||
end
|
||||
|
||||
factory :google_play_integration, class: 'Integrations::GooglePlay' do
|
||||
|
|
|
|||
|
|
@ -1,103 +1,196 @@
|
|||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import NewEnvironment from '~/environments/components/new_environment.vue';
|
||||
import createEnvironment from '~/environments/graphql/mutations/create_environment.mutation.graphql';
|
||||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import createMockApollo from '../__helpers__/mock_apollo_helper';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility');
|
||||
jest.mock('~/alert');
|
||||
|
||||
const DEFAULT_OPTS = {
|
||||
provide: {
|
||||
projectEnvironmentsPath: '/projects/environments',
|
||||
protectedEnvironmentSettingsPath: '/projects/not_real/settings/ci_cd',
|
||||
},
|
||||
const newName = 'test';
|
||||
const newExternalUrl = 'https://google.ca';
|
||||
|
||||
const provide = {
|
||||
projectEnvironmentsPath: '/projects/environments',
|
||||
projectPath: '/path/to/project',
|
||||
};
|
||||
|
||||
const environmentCreate = { environment: { id: '1', path: 'path/to/environment' }, errors: [] };
|
||||
const environmentCreateError = {
|
||||
environment: null,
|
||||
errors: [{ message: 'uh oh!' }],
|
||||
};
|
||||
|
||||
describe('~/environments/components/new.vue', () => {
|
||||
let wrapper;
|
||||
let mock;
|
||||
let name;
|
||||
let url;
|
||||
let form;
|
||||
|
||||
const createWrapper = (opts = {}) =>
|
||||
mountExtended(NewEnvironment, {
|
||||
...DEFAULT_OPTS,
|
||||
...opts,
|
||||
const createMockApolloProvider = (mutationResult) => {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
return createMockApollo([
|
||||
[
|
||||
createEnvironment,
|
||||
jest.fn().mockResolvedValue({ data: { environmentCreate: mutationResult } }),
|
||||
],
|
||||
]);
|
||||
};
|
||||
|
||||
const createWrapperWithApollo = async (mutationResult = environmentCreate) => {
|
||||
wrapper = mountExtended(NewEnvironment, {
|
||||
provide: {
|
||||
...provide,
|
||||
glFeatures: {
|
||||
environmentSettingsToGraphql: true,
|
||||
},
|
||||
},
|
||||
apolloProvider: createMockApolloProvider(mutationResult),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
wrapper = createWrapper();
|
||||
name = wrapper.findByLabelText('Name');
|
||||
url = wrapper.findByLabelText('External URL');
|
||||
form = wrapper.findByRole('form', { name: 'New environment' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
const showsLoading = () => wrapper.findComponent(GlLoadingIcon).exists();
|
||||
|
||||
const submitForm = async (expected, response) => {
|
||||
mock
|
||||
.onPost(DEFAULT_OPTS.provide.projectEnvironmentsPath, {
|
||||
name: expected.name,
|
||||
external_url: expected.url,
|
||||
})
|
||||
.reply(...response);
|
||||
await name.setValue(expected.name);
|
||||
await url.setValue(expected.url);
|
||||
|
||||
await form.trigger('submit');
|
||||
await waitForPromises();
|
||||
};
|
||||
|
||||
it('sets the title to New environment', () => {
|
||||
const header = wrapper.findByRole('heading', { name: 'New environment' });
|
||||
expect(header.exists()).toBe(true);
|
||||
const createWrapperWithAxios = () => {
|
||||
wrapper = mountExtended(NewEnvironment, {
|
||||
provide: {
|
||||
...provide,
|
||||
glFeatures: {
|
||||
environmentSettingsToGraphql: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findNameInput = () => wrapper.findByLabelText(__('Name'));
|
||||
const findExternalUrlInput = () => wrapper.findByLabelText(__('External URL'));
|
||||
const findForm = () => wrapper.findByRole('form', { name: __('New environment') });
|
||||
const showsLoading = () => wrapper.findComponent(GlLoadingIcon).exists();
|
||||
|
||||
const submitForm = async () => {
|
||||
await findNameInput().setValue('test');
|
||||
await findExternalUrlInput().setValue('https://google.ca');
|
||||
|
||||
await findForm().trigger('submit');
|
||||
};
|
||||
|
||||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
createWrapperWithAxios();
|
||||
});
|
||||
|
||||
it('sets the title to New environment', () => {
|
||||
const header = wrapper.findByRole('heading', { name: 'New environment' });
|
||||
expect(header.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it.each`
|
||||
input | value
|
||||
${() => findNameInput()} | ${'test'}
|
||||
${() => findExternalUrlInput()} | ${'https://example.org'}
|
||||
`('changes the value of the input to $value', ({ input, value }) => {
|
||||
input().setValue(value);
|
||||
|
||||
expect(input().element.value).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
it.each`
|
||||
input | value
|
||||
${() => name} | ${'test'}
|
||||
${() => url} | ${'https://example.org'}
|
||||
`('changes the value of the input to $value', async ({ input, value }) => {
|
||||
await input().setValue(value);
|
||||
describe('when environmentSettingsToGraphql feature is enabled', () => {
|
||||
describe('when mutation successful', () => {
|
||||
beforeEach(() => {
|
||||
createWrapperWithApollo();
|
||||
});
|
||||
|
||||
expect(input().element.value).toBe(value);
|
||||
it('shows loader after form is submitted', async () => {
|
||||
expect(showsLoading()).toBe(false);
|
||||
|
||||
await submitForm();
|
||||
|
||||
expect(showsLoading()).toBe(true);
|
||||
});
|
||||
|
||||
it('submits the new environment on submit', async () => {
|
||||
submitForm();
|
||||
await waitForPromises();
|
||||
|
||||
expect(visitUrl).toHaveBeenCalledWith('path/to/environment');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when failed', () => {
|
||||
beforeEach(async () => {
|
||||
createWrapperWithApollo(environmentCreateError);
|
||||
submitForm();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('shows errors on error', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: 'uh oh!' });
|
||||
expect(showsLoading()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('shows loader after form is submitted', async () => {
|
||||
const expected = { name: 'test', url: 'https://google.ca' };
|
||||
describe('when environmentSettingsToGraphql feature is disabled', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
createWrapperWithAxios();
|
||||
});
|
||||
|
||||
expect(showsLoading()).toBe(false);
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
await submitForm(expected, [HTTP_STATUS_OK, { path: '/test' }]);
|
||||
it('shows loader after form is submitted', async () => {
|
||||
expect(showsLoading()).toBe(false);
|
||||
|
||||
expect(showsLoading()).toBe(true);
|
||||
});
|
||||
mock
|
||||
.onPost(provide.projectEnvironmentsPath, {
|
||||
name: newName,
|
||||
external_url: newExternalUrl,
|
||||
})
|
||||
.reply(HTTP_STATUS_OK, { path: '/test' });
|
||||
|
||||
it('submits the new environment on submit', async () => {
|
||||
const expected = { name: 'test', url: 'https://google.ca' };
|
||||
await submitForm();
|
||||
|
||||
await submitForm(expected, [HTTP_STATUS_OK, { path: '/test' }]);
|
||||
expect(showsLoading()).toBe(true);
|
||||
});
|
||||
|
||||
expect(visitUrl).toHaveBeenCalledWith('/test');
|
||||
});
|
||||
it('submits the new environment on submit', async () => {
|
||||
mock
|
||||
.onPost(provide.projectEnvironmentsPath, {
|
||||
name: newName,
|
||||
external_url: newExternalUrl,
|
||||
})
|
||||
.reply(HTTP_STATUS_OK, { path: '/test' });
|
||||
|
||||
it('shows errors on error', async () => {
|
||||
const expected = { name: 'test', url: 'https://google.ca' };
|
||||
await submitForm();
|
||||
await waitForPromises();
|
||||
|
||||
await submitForm(expected, [HTTP_STATUS_BAD_REQUEST, { message: ['name taken'] }]);
|
||||
expect(visitUrl).toHaveBeenCalledWith('/test');
|
||||
});
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: 'name taken' });
|
||||
expect(showsLoading()).toBe(false);
|
||||
it('shows errors on error', async () => {
|
||||
mock
|
||||
.onPost(provide.projectEnvironmentsPath, {
|
||||
name: newName,
|
||||
external_url: newExternalUrl,
|
||||
})
|
||||
.reply(HTTP_STATUS_BAD_REQUEST, { message: ['name taken'] });
|
||||
|
||||
await submitForm();
|
||||
await waitForPromises();
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: 'name taken' });
|
||||
expect(showsLoading()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -367,19 +367,12 @@ describe('ErrorTrackingList', () => {
|
|||
const emptyStatePrimaryDescription = emptyStateComponent.find('span', {
|
||||
exactText: 'Monitor your errors directly in GitLab.',
|
||||
});
|
||||
const emptyStateSecondaryDescription = emptyStateComponent.find('span', {
|
||||
exactText: 'Error tracking is currently in',
|
||||
});
|
||||
const emptyStateLinks = emptyStateComponent.findAll('a');
|
||||
expect(emptyStateComponent.isVisible()).toBe(true);
|
||||
expect(emptyStatePrimaryDescription.exists()).toBe(true);
|
||||
expect(emptyStateSecondaryDescription.exists()).toBe(true);
|
||||
expect(emptyStateLinks.at(0).attributes('href')).toBe(
|
||||
'/help/operations/error_tracking.html#integrated-error-tracking',
|
||||
);
|
||||
expect(emptyStateLinks.at(1).attributes('href')).toBe(
|
||||
'https://about.gitlab.com/handbook/product/gitlab-the-product/#open-beta',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
import { GlAvatar, GlSprintf, GlIcon } from '@gitlab/ui';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import {
|
||||
VISIBILITY_LEVEL_PRIVATE_STRING,
|
||||
VISIBILITY_LEVEL_INTERNAL_STRING,
|
||||
VISIBILITY_LEVEL_PUBLIC_STRING,
|
||||
} from '~/visibility_level/constants';
|
||||
import { SNIPPET_VISIBILITY } from '~/snippets/constants';
|
||||
import SnippetRow from '~/profile/components/snippets/snippet_row.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { MOCK_USER, MOCK_SNIPPET } from 'jest/profile/mock_data';
|
||||
|
|
@ -16,16 +24,123 @@ describe('UserProfileSnippetRow', () => {
|
|||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findGlAvatar = () => wrapper.findComponent(GlAvatar);
|
||||
const findSnippetUrl = () => wrapper.findByTestId('snippet-url');
|
||||
const findSnippetId = () => wrapper.findByTestId('snippet-id');
|
||||
const findSnippetCreatedAt = () => wrapper.findByTestId('snippet-created-at');
|
||||
const findSnippetAuthor = () => wrapper.findByTestId('snippet-author');
|
||||
const findSnippetBlob = () => wrapper.findByTestId('snippet-blob');
|
||||
const findSnippetComments = () => wrapper.findByTestId('snippet-comments');
|
||||
const findSnippetVisibility = () => wrapper.findByTestId('snippet-visibility');
|
||||
const findSnippetUpdatedAt = () => wrapper.findByTestId('snippet-updated-at');
|
||||
|
||||
describe('template', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders snippet title', () => {
|
||||
expect(wrapper.text()).toBe(MOCK_SNIPPET.title);
|
||||
it('renders GlAvatar with user avatar', () => {
|
||||
expect(findGlAvatar().exists()).toBe(true);
|
||||
expect(findGlAvatar().attributes('src')).toBe(MOCK_USER.avatarUrl);
|
||||
});
|
||||
|
||||
it('renders Snippet Url with snippet webUrl', () => {
|
||||
expect(findSnippetUrl().exists()).toBe(true);
|
||||
expect(findSnippetUrl().attributes('href')).toBe(MOCK_SNIPPET.webUrl);
|
||||
});
|
||||
|
||||
it('renders Snippet ID correctly formatted', () => {
|
||||
expect(findSnippetId().exists()).toBe(true);
|
||||
expect(findSnippetId().text()).toBe(`$${getIdFromGraphQLId(MOCK_SNIPPET.id)}`);
|
||||
});
|
||||
|
||||
it('renders Snippet Created At with correct date string', () => {
|
||||
expect(findSnippetCreatedAt().exists()).toBe(true);
|
||||
expect(findSnippetCreatedAt().attributes('time')).toBe(MOCK_SNIPPET.createdAt.toString());
|
||||
});
|
||||
|
||||
it('renders Snippet Author with profileLink', () => {
|
||||
expect(findSnippetAuthor().exists()).toBe(true);
|
||||
expect(findSnippetAuthor().attributes('href')).toBe(`/${MOCK_USER.username}`);
|
||||
});
|
||||
|
||||
it('renders Snippet Updated At with correct date string', () => {
|
||||
expect(findSnippetUpdatedAt().exists()).toBe(true);
|
||||
expect(findSnippetUpdatedAt().attributes('time')).toBe(MOCK_SNIPPET.updatedAt.toString());
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
nodes | hasOpacity | tooltip
|
||||
${[]} | ${true} | ${'0 files'}
|
||||
${[{ name: 'file.txt' }]} | ${false} | ${'1 file'}
|
||||
${[{ name: 'file.txt' }, { name: 'file2.txt' }]} | ${false} | ${'2 files'}
|
||||
`('Blob Icon', ({ nodes, hasOpacity, tooltip }) => {
|
||||
describe(`when blobs length ${nodes.length}`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ snippet: { ...MOCK_SNIPPET, blobs: { nodes } } });
|
||||
});
|
||||
|
||||
it(`does${hasOpacity ? '' : ' not'} render icon with opacity`, () => {
|
||||
expect(findSnippetBlob().findComponent(GlIcon).props('name')).toBe('documents');
|
||||
expect(findSnippetBlob().classes('gl-opacity-5')).toBe(hasOpacity);
|
||||
});
|
||||
|
||||
it('renders text and tooltip correctly', () => {
|
||||
expect(findSnippetBlob().text()).toBe(nodes.length.toString());
|
||||
expect(findSnippetBlob().attributes('title')).toBe(tooltip);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
nodes | hasOpacity
|
||||
${[]} | ${true}
|
||||
${[{ id: 'note/1' }]} | ${false}
|
||||
${[{ id: 'note/1' }, { id: 'note/2' }]} | ${false}
|
||||
`('Comments Icon', ({ nodes, hasOpacity }) => {
|
||||
describe(`when comments length ${nodes.length}`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ snippet: { ...MOCK_SNIPPET, notes: { nodes } } });
|
||||
});
|
||||
|
||||
it(`does${hasOpacity ? '' : ' not'} render icon with opacity`, () => {
|
||||
expect(findSnippetComments().findComponent(GlIcon).props('name')).toBe('comments');
|
||||
expect(findSnippetComments().classes('gl-opacity-5')).toBe(hasOpacity);
|
||||
});
|
||||
|
||||
it('renders text correctly', () => {
|
||||
expect(findSnippetComments().text()).toBe(nodes.length.toString());
|
||||
});
|
||||
|
||||
it('renders link to comments correctly', () => {
|
||||
expect(findSnippetComments().attributes('href')).toBe(`${MOCK_SNIPPET.webUrl}#notes`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
visibilityLevel
|
||||
${VISIBILITY_LEVEL_PUBLIC_STRING}
|
||||
${VISIBILITY_LEVEL_PRIVATE_STRING}
|
||||
${VISIBILITY_LEVEL_INTERNAL_STRING}
|
||||
`('Visibility Icon', ({ visibilityLevel }) => {
|
||||
describe(`when visibilityLevel is ${visibilityLevel}`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ snippet: { ...MOCK_SNIPPET, visibilityLevel } });
|
||||
});
|
||||
|
||||
it(`renders the ${SNIPPET_VISIBILITY[visibilityLevel].icon} icon`, () => {
|
||||
expect(findSnippetVisibility().findComponent(GlIcon).props('name')).toBe(
|
||||
SNIPPET_VISIBILITY[visibilityLevel].icon,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ const getMockSnippet = (id) => {
|
|||
},
|
||||
],
|
||||
},
|
||||
commenters: {
|
||||
notes: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'git://gitlab/User/1',
|
||||
id: 'git://gitlab/Note/1',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::ErrorTracking::Processor::GrpcErrorProcessor, :sentry do
|
||||
RSpec.describe Gitlab::ErrorTracking::Processor::GrpcErrorProcessor, :sentry, feature_category: :integrations do
|
||||
describe '.call' do
|
||||
let(:raven_required_options) do
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3870,7 +3870,9 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
|
|||
end
|
||||
|
||||
context 'for the apple_app_store integration' do
|
||||
let_it_be(:apple_app_store_integration) { create(:apple_app_store_integration) }
|
||||
before do
|
||||
allow(build.pipeline).to receive(:protected_ref?).and_return(pipeline_protected_ref)
|
||||
end
|
||||
|
||||
let(:apple_app_store_variables) do
|
||||
[
|
||||
|
|
@ -3881,41 +3883,72 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
|
|||
]
|
||||
end
|
||||
|
||||
context 'when the apple_app_store exists' do
|
||||
context 'when a build is protected' do
|
||||
before do
|
||||
allow(build.pipeline).to receive(:protected_ref?).and_return(true)
|
||||
build.project.update!(apple_app_store_integration: apple_app_store_integration)
|
||||
end
|
||||
|
||||
it 'includes apple_app_store variables' do
|
||||
is_expected.to include(*apple_app_store_variables)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a build is not protected' do
|
||||
before do
|
||||
allow(build.pipeline).to receive(:protected_ref?).and_return(false)
|
||||
build.project.update!(apple_app_store_integration: apple_app_store_integration)
|
||||
end
|
||||
|
||||
it 'does not include the apple_app_store variables' do
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_ISSUER_ID' }).to be_nil
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_KEY' }).to be_nil
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_KEY_ID' }).to be_nil
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64' }).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the apple_app_store integration does not exist' do
|
||||
it 'does not include apple_app_store variables' do
|
||||
shared_examples 'does not include the apple_app_store variables' do
|
||||
specify do
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_ISSUER_ID' }).to be_nil
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_KEY' }).to be_nil
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_KEY_ID' }).to be_nil
|
||||
expect(subject.find { |v| v[:key] == 'APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64' }).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'includes apple_app_store variables' do
|
||||
specify do
|
||||
expect(subject).to include(*apple_app_store_variables)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an Apple App Store integration exists' do
|
||||
let_it_be(:apple_app_store_integration) do
|
||||
create(:apple_app_store_integration, project: project)
|
||||
end
|
||||
|
||||
context 'when app_store_protected_refs is true' do
|
||||
context 'when a build is protected' do
|
||||
let(:pipeline_protected_ref) { true }
|
||||
|
||||
include_examples 'includes apple_app_store variables'
|
||||
end
|
||||
|
||||
context 'when a build is not protected' do
|
||||
let(:pipeline_protected_ref) { false }
|
||||
|
||||
include_examples 'does not include the apple_app_store variables'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when app_store_protected_refs is false' do
|
||||
before do
|
||||
apple_app_store_integration.update!(app_store_protected_refs: false)
|
||||
end
|
||||
|
||||
context 'when a build is protected' do
|
||||
let(:pipeline_protected_ref) { true }
|
||||
|
||||
include_examples 'includes apple_app_store variables'
|
||||
end
|
||||
|
||||
context 'when a build is not protected' do
|
||||
let(:pipeline_protected_ref) { false }
|
||||
|
||||
include_examples 'includes apple_app_store variables'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an Apple App Store integration does not exist' do
|
||||
context 'when a build is protected' do
|
||||
let(:pipeline_protected_ref) { true }
|
||||
|
||||
include_examples 'does not include the apple_app_store variables'
|
||||
end
|
||||
|
||||
context 'when a build is not protected' do
|
||||
let(:pipeline_protected_ref) { false }
|
||||
|
||||
include_examples 'does not include the apple_app_store variables'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for the google_play integration' do
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ RSpec.describe Integrations::AppleAppStore, feature_category: :mobile_devops do
|
|||
it { is_expected.to validate_presence_of :app_store_key_id }
|
||||
it { is_expected.to validate_presence_of :app_store_private_key }
|
||||
it { is_expected.to validate_presence_of :app_store_private_key_file_name }
|
||||
it { is_expected.to allow_value(true).for(:app_store_protected_refs) }
|
||||
it { is_expected.to allow_value(false).for(:app_store_protected_refs) }
|
||||
it { is_expected.not_to allow_value(nil).for(:app_store_protected_refs) }
|
||||
it { is_expected.to allow_value('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee').for(:app_store_issuer_id) }
|
||||
it { is_expected.not_to allow_value('abcde').for(:app_store_issuer_id) }
|
||||
it { is_expected.to allow_value(File.read('spec/fixtures/ssl_key.pem')).for(:app_store_private_key) }
|
||||
|
|
@ -30,7 +33,7 @@ RSpec.describe Integrations::AppleAppStore, feature_category: :mobile_devops do
|
|||
describe '#fields' do
|
||||
it 'returns custom fields' do
|
||||
expect(apple_app_store_integration.fields.pluck(:name)).to match_array(%w[app_store_issuer_id app_store_key_id
|
||||
app_store_private_key app_store_private_key_file_name])
|
||||
app_store_private_key app_store_private_key_file_name app_store_protected_refs])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -62,8 +65,8 @@ RSpec.describe Integrations::AppleAppStore, feature_category: :mobile_devops do
|
|||
describe '#ci_variables' do
|
||||
let(:apple_app_store_integration) { build_stubbed(:apple_app_store_integration) }
|
||||
|
||||
it 'returns vars when the integration is activated' do
|
||||
ci_vars = [
|
||||
let(:ci_vars) do
|
||||
[
|
||||
{
|
||||
key: 'APP_STORE_CONNECT_API_KEY_ISSUER_ID',
|
||||
value: apple_app_store_integration.app_store_issuer_id,
|
||||
|
|
@ -89,13 +92,32 @@ RSpec.describe Integrations::AppleAppStore, feature_category: :mobile_devops do
|
|||
public: false
|
||||
}
|
||||
]
|
||||
|
||||
expect(apple_app_store_integration.ci_variables).to match_array(ci_vars)
|
||||
end
|
||||
|
||||
it 'returns an empty array when the integration is disabled' do
|
||||
apple_app_store_integration = build_stubbed(:apple_app_store_integration, active: false)
|
||||
expect(apple_app_store_integration.ci_variables).to match_array([])
|
||||
it 'returns the vars for protected branch' do
|
||||
expect(apple_app_store_integration.ci_variables(protected_ref: true)).to match_array(ci_vars)
|
||||
end
|
||||
|
||||
it 'doesn\'t return the vars for unprotected branch' do
|
||||
expect(apple_app_store_integration.ci_variables(protected_ref: false)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#initialize_properties' do
|
||||
context 'when app_store_protected_refs is nil' do
|
||||
let(:apple_app_store_integration) { described_class.new(app_store_protected_refs: nil) }
|
||||
|
||||
it 'sets app_store_protected_refs to true' do
|
||||
expect(apple_app_store_integration.app_store_protected_refs).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when app_store_protected_refs is false' do
|
||||
let(:apple_app_store_integration) { build(:apple_app_store_integration, app_store_protected_refs: false) }
|
||||
|
||||
it 'sets app_store_protected_refs to false' do
|
||||
expect(apple_app_store_integration.app_store_protected_refs).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -105,7 +127,7 @@ RSpec.describe Integrations::AppleAppStore, feature_category: :mobile_devops do
|
|||
|
||||
describe '#ci_variables' do
|
||||
it 'returns an empty array' do
|
||||
expect(apple_app_store_integration.ci_variables).to match_array([])
|
||||
expect(apple_app_store_integration.ci_variables(protected_ref: true)).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ RSpec.shared_context 'with integration' do
|
|||
hash.merge!(k => 'ABC1')
|
||||
elsif integration == 'apple_app_store' && k == :app_store_private_key_file_name
|
||||
hash.merge!(k => 'ssl_key.pem')
|
||||
elsif integration == 'apple_app_store' && k == :app_store_protected_refs # rubocop:disable Lint/DuplicateBranch
|
||||
hash.merge!(k => true)
|
||||
elsif integration == 'google_play' && k == :package_name
|
||||
hash.merge!(k => 'com.gitlab.foo.bar')
|
||||
elsif integration == 'google_play' && k == :service_account_key
|
||||
|
|
|
|||
Loading…
Reference in New Issue