diff options
22 files changed, 287 insertions, 163 deletions
diff --git a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml index a04d81fb342..d51e255fb95 100644 --- a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml +++ b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml @@ -57,7 +57,7 @@ dont-interrupt-me: --volume $CI_PROJECT_DIR/test_output:/home/gdk/gdk/gitlab/qa/tmp:z \ --volume $CI_PROJECT_DIR/logs/gdk:/home/gdk/gdk/log \ --volume $CI_PROJECT_DIR/logs/gitlab:/home/gdk/gdk/gitlab/log \ - ${QA_GDK_IMAGE} "${CI_COMMIT_REF_SLUG}" "$TEST_GDK_TAGS --tag ~requires_praefect" || true + ${QA_GDK_IMAGE} "${CI_COMMIT_SHA}" "$TEST_GDK_TAGS --tag ~requires_praefect" || true - echo -e "\e[0Ksection_end:`date +%s`:launch_gdk_and_tests\r\e[0K" allow_failure: true diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index 21841680cab..9c04a72375b 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -35,9 +35,6 @@ export default { isAuthoredByMe() { return this.noteAuthorId === this.getUserData.id; }, - addButtonClass() { - return this.isAuthoredByMe ? 'js-user-authored' : ''; - }, }, methods: { ...mapActions(['toggleAwardRequest']), @@ -64,7 +61,6 @@ export default { :awards="awards" :can-award-emoji="canAwardEmoji" :current-user-id="getUserData.id" - :add-button-class="addButtonClass" @award="handleAward($event)" /> </div> diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 60df042529a..4ce714f7c21 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -144,7 +144,7 @@ export default { }, computed: { shouldRenderGenie() { - return this.glFeatures.explainCode && this.glFeatures.explainCodeSnippet; + return this.glFeatures.explainCode && this.glFeatures.explainCodeSnippet && this.isLoggedIn; }, isLoggedIn() { return isLoggedIn(); diff --git a/app/assets/javascripts/super_sidebar/components/pinned_section.vue b/app/assets/javascripts/super_sidebar/components/pinned_section.vue index 193de143c2b..164ea04ab8e 100644 --- a/app/assets/javascripts/super_sidebar/components/pinned_section.vue +++ b/app/assets/javascripts/super_sidebar/components/pinned_section.vue @@ -2,6 +2,8 @@ import { GlCollapse, GlIcon } from '@gitlab/ui'; import Draggable from 'vuedraggable'; import { s__ } from '~/locale'; +import { setCookie, getCookie } from '~/lib/utils/common_utils'; +import { SIDEBAR_PINS_EXPANDED_COOKIE, SIDEBAR_COOKIE_EXPIRATION } from '../constants'; import NavItem from './nav_item.vue'; export default { @@ -25,7 +27,7 @@ export default { }, data() { return { - expanded: true, + expanded: getCookie(SIDEBAR_PINS_EXPANDED_COOKIE) !== 'false', draggableItems: this.items, }; }, @@ -38,6 +40,11 @@ export default { }, }, watch: { + expanded(newExpanded) { + setCookie(SIDEBAR_PINS_EXPANDED_COOKIE, newExpanded, { + expires: SIDEBAR_COOKIE_EXPIRATION, + }); + }, items(newItems) { this.draggableItems = newItems; }, diff --git a/app/assets/javascripts/super_sidebar/constants.js b/app/assets/javascripts/super_sidebar/constants.js index 4e25b9cff45..3dbd12b4ff7 100644 --- a/app/assets/javascripts/super_sidebar/constants.js +++ b/app/assets/javascripts/super_sidebar/constants.js @@ -30,3 +30,6 @@ export const HELP_MENU_TRACKING_DEFAULTS = { 'data-track-property': 'nav_help_menu', 'data-track-action': 'click_link', }; + +export const SIDEBAR_PINS_EXPANDED_COOKIE = 'sidebar_pinned_section_expanded'; +export const SIDEBAR_COOKIE_EXPIRATION = 365 * 10; diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue index cb38b3e13bb..8f1f7ba0ad8 100644 --- a/app/assets/javascripts/vue_shared/components/awards_list.vue +++ b/app/assets/javascripts/vue_shared/components/awards_list.vue @@ -35,11 +35,6 @@ export default { required: false, default: NO_USER_ID, }, - addButtonClass: { - type: String, - required: false, - default: '', - }, defaultAwards: { type: Array, required: false, @@ -50,6 +45,11 @@ export default { required: false, default: 'selected', }, + boundary: { + type: String, + required: false, + default: '', + }, }, data() { return { @@ -201,6 +201,8 @@ export default { v-gl-tooltip.viewport :title="__('Add reaction')" :toggle-class="['add-reaction-button btn-icon gl-relative!', { 'is-active': isMenuOpen }]" + :right="false" + :boundary="boundary" data-testid="emoji-picker" @click="handleAward" @shown="setIsMenuOpen(true)" diff --git a/config/feature_flags/development/import_project_from_remote_file_s3.yml b/config/feature_flags/development/sync_approval_rules_from_findings.yml index c7d52202726..e09593b17a6 100644 --- a/config/feature_flags/development/import_project_from_remote_file_s3.yml +++ b/config/feature_flags/development/sync_approval_rules_from_findings.yml @@ -1,8 +1,8 @@ --- -name: import_project_from_remote_file_s3 -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77259 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350571 -milestone: '14.9' +name: sync_approval_rules_from_findings +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115825 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/397011 +milestone: '15.11' type: development -group: group::import +group: group::security policies default_enabled: false diff --git a/config/initializers/carrierwave_patch.rb b/config/initializers/carrierwave_patch.rb index cb476d9f01a..449f4b4a607 100644 --- a/config/initializers/carrierwave_patch.rb +++ b/config/initializers/carrierwave_patch.rb @@ -22,7 +22,7 @@ module CarrierWave # multithreaded uploads (https://github.com/fog/fog-aws/pull/579). # Multithreaded uploads are essential for copying large amounts of data # within the request timeout. - if ::Feature.enabled?(:s3_multithreaded_uploads) && fog_provider == 'AWS' + if ::Feature.enabled?(:s3_multithreaded_uploads, type: :ops) && fog_provider == 'AWS' # AWS SDK uses 10 threads by default and a multipart chunk size of 10 MB file.concurrency = 10 file.multipart_chunk_size = 10485760 diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index c97f920af3d..e6d88904341 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -959,6 +959,7 @@ Input type: `AiActionInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationaiactionclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationaiactionexplaincode"></a>`explainCode` | [`AiExplainCodeInput`](#aiexplaincodeinput) | Input for explain_code AI action. | | <a id="mutationaiactionsummarizecomments"></a>`summarizeComments` | [`AiSummarizeCommentsInput`](#aisummarizecommentsinput) | Input for summarize_comments AI action. | #### Fields @@ -26480,6 +26481,24 @@ be used as arguments). Only general use input types are listed here. For mutation input types, see the associated mutation type above. +### `AiExplainCodeInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="aiexplaincodeinputmessages"></a>`messages` | [`[AiExplainCodeMessageInput!]!`](#aiexplaincodemessageinput) | Code messages that is passed to be explained by AI. | +| <a id="aiexplaincodeinputresourceid"></a>`resourceId` | [`AiModelID!`](#aimodelid) | GID of the resource to mutate. | + +### `AiExplainCodeMessageInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="aiexplaincodemessageinputcontent"></a>`content` | [`String!`](#string) | Content of the message. | +| <a id="aiexplaincodemessageinputrole"></a>`role` | [`String!`](#string) | Role of the message (system, user, assistant). | + ### `AiSummarizeCommentsInput` #### Arguments diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md index 22df2b2ef4a..0e52bb959cb 100644 --- a/doc/api/project_import_export.md +++ b/doc/api/project_import_export.md @@ -255,9 +255,7 @@ The `Content-Type` header must be `application/gzip`. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348874) in GitLab 14.9 in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta), [with a flag](../administration/feature_flags.md) named `import_project_from_remote_file_s3`. Disabled by default. > - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/348874) in GitLab 14.10. - -FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `import_project_from_remote_file_s3`. On GitLab.com, this feature is available. +> - [Enabled globally](https://gitlab.com/gitlab-org/gitlab/-/issues/350571) in GitLab 15.11. ```plaintext POST /projects/remote-import-s3 diff --git a/doc/architecture/blueprints/secret_detection/index.md b/doc/architecture/blueprints/secret_detection/index.md index 9911fd04667..de240646b37 100644 --- a/doc/architecture/blueprints/secret_detection/index.md +++ b/doc/architecture/blueprints/secret_detection/index.md @@ -170,20 +170,20 @@ sequenceDiagram ## Iterations -- [x] Define [requirements for detection coverage and actions](https://gitlab.com/gitlab-org/gitlab/-/issues/376716) -- [x] Implement [Clientside detection of GitLab tokens within comments/issues](https://gitlab.com/gitlab-org/gitlab/-/issues/368434) -- [ ] PoC of secret scanning service - - [ ] Benchmarking of issuables, comments, job logs and blobs to gain confidence that the total costs will be viable - - [ ] Capacity planning for addition of service component to Reference Architectures headroom - - [ ] Service capabilities - - [ ] gRPC commit retrieval from Gitaly - - [ ] blob scanning -- [ ] Implementation of secret scanning service MVC (targeting individual commits) -- [ ] Security and readiness review -- [ ] Deployment and monitoring -- [ ] Implementation of secret scanning service MVC (targeting arbitrary text blobs) -- [ ] Deployment and monitoring -- [ ] High priority domain object rollout (priority `TBD`) - - [ ] Issuable comments - - [ ] Issuable bodies - - [ ] Job logs +- ✓ Define [requirements for detection coverage and actions](https://gitlab.com/gitlab-org/gitlab/-/issues/376716) +- ✓ Implement [Clientside detection of GitLab tokens within comments/issues](https://gitlab.com/gitlab-org/gitlab/-/issues/368434) +- PoC of secret scanning service + - Benchmarking of issuables, comments, job logs and blobs to gain confidence that the total costs will be viable + - Capacity planning for addition of service component to Reference Architectures headroom + - Service capabilities + - gRPC commit retrieval from Gitaly + - blob scanning +- Implementation of secret scanning service MVC (targeting individual commits) +- Security and readiness review +- Deployment and monitoring +- Implementation of secret scanning service MVC (targeting arbitrary text blobs) +- Deployment and monitoring +- High priority domain object rollout (priority `TBD`) + - Issuable comments + - Issuable bodies + - Job logs diff --git a/doc/development/sec/security_report_ingestion_overview.md b/doc/development/sec/security_report_ingestion_overview.md index 492d840e800..aca33990b0f 100644 --- a/doc/development/sec/security_report_ingestion_overview.md +++ b/doc/development/sec/security_report_ingestion_overview.md @@ -12,7 +12,7 @@ The `Vulnerability::Feedback` model is currently undergoing deprecation and shou ## Commonly used terms -### Feedback +### Feedback An instance of `Vulnerabilities::Feedback` class. They are created to keep track of users' interactions with Vulnerability Findings before they are promoted to a Vulnerability. This model is deprecated and due to be removed by GitLab 16.0 as part of the [Deprecate and remove Vulnerabilities::Feedback epic](https://gitlab.com/groups/gitlab-org/-/epics/5629). @@ -38,7 +38,7 @@ An instance of the `Vulnerabilities::StateTransition` class. This model represen ### Vulnerability -An instance of `Vulnerability` class. A `Vulnerability` is representative of a `Vulnerability::Finding` which has been detected in the default branch of the project, or if the `present_on_default_branch` flag is false, is representative of a finding which has been interacted with in some way outside of the default branch, such as if it is dismissed (`State Transition`), or linked to an `Issue` or `Merge Request`. They are created based on information available in `Vulnerabilities::Finding` class. Every `Vulnerability` **must have** a corresponding `Vulnerabilities::Finding` object to be valid, however this is not enforced at the database level. +An instance of `Vulnerability` class. A `Vulnerability` is representative of a `Vulnerability::Finding` which has been detected in the default branch of the project, or if the `present_on_default_branch` flag is false, is representative of a finding which has been interacted with in some way outside of the default branch, such as if it is dismissed (`State Transition`), or linked to an `Issue` or `Merge Request`. They are created based on information available in `Vulnerabilities::Finding` class. Every `Vulnerability` **must have** a corresponding `Vulnerabilities::Finding` object to be valid, however this is not enforced at the database level. ### Finding @@ -81,7 +81,7 @@ At this point, the following things can happen to the `Security::Finding` which ### Scan runs in a pipeline for the default branch -If the pipeline ran on the default branch then the following steps, in addition to the steps in [#scan-runs-in-a-pipeline-for-a-non-default-branch], are executed: +If the pipeline ran on the default branch then the following steps, in addition to the steps in [Scan runs in a pipeline for a non-default branch](#scan-runs-in-a-pipeline-for-a-non-default-branch), are executed: 1. `Security::StoreScansService` gets called and schedules `StoreSecurityReportsWorker`. 1. `StoreSecurityReportsWorker` executes `Security::Ingestion::IngestReportsService`. @@ -92,8 +92,8 @@ If the pipeline ran on the default branch then the following steps, in addition If you change the state of a vulnerability, such as selecting `Dismiss vulnerability` the following things currently happen: -- A `Feedback` record of `dismissal` type is created to record the current state. -- If they do not already exist, a `Vulnerability Finding` and a `Vulnerability` with `present_on_default_branch: false` attribute get created, to which a `State Transition` reflecting the state change is related. +- A `Feedback` record of `dismissal` type is created to record the current state. +- If they do not already exist, a `Vulnerability Finding` and a `Vulnerability` with `present_on_default_branch: false` attribute get created, to which a `State Transition` reflecting the state change is related. You can optionally add a comment to the state change which is recorded on both the `Feedback` and the `State Transition`. diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 02f0d9a2a70..a00ef7144d4 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -217,8 +217,6 @@ module API ] end post 'remote-import-s3' do - not_found! unless ::Feature.enabled?(:import_project_from_remote_file_s3) - check_rate_limit! :project_import, scope: [current_user, :project_import] response = ::Import::GitlabProjects::CreateProjectService.new( diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 10536863947..e8e6a120c77 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1863,6 +1863,9 @@ msgstr "" msgid "AI|What does the selected code mean?" msgstr "" +msgid "AI|You are not allowed to copy any part of this output into issues, comments, GitLab source code, commit messages, merge requests or any other user interface in the %{gitlabOrg} or %{gitlabCom} groups." +msgstr "" + msgid "API" msgstr "" diff --git a/qa/gdk/launch b/qa/gdk/launch index 4b1fc6ae191..8ad2ce7e5ad 100755 --- a/qa/gdk/launch +++ b/qa/gdk/launch @@ -1,6 +1,6 @@ #!/bin/bash -COMMIT_REF=${1:-$CI_COMMIT_REF_SLUG} +COMMIT_REF=${1:-$CI_COMMIT_SHA} RSPEC_ARGS=$2 if [ -z "${COMMIT_REF}" ]; then diff --git a/spec/features/nav/pinned_nav_items_spec.rb b/spec/features/nav/pinned_nav_items_spec.rb index b4eea931857..fa8224848f9 100644 --- a/spec/features/nav/pinned_nav_items_spec.rb +++ b/spec/features/nav/pinned_nav_items_spec.rb @@ -54,6 +54,26 @@ RSpec.describe 'Navigation menu item pinning', :js, feature_category: :navigatio end end + describe 'collapsible section' do + it 'shows the Pinned section as expanded by default' do + within '#super-sidebar' do + expect(page).to have_content 'Your pinned items appear here.' + end + end + + it 'maintains the collapsed/expanded state between page loads' do + within '#super-sidebar' do + click_on 'Pinned' + visit project_path(project) + expect(page).not_to have_content 'Your pinned items appear here.' + + click_on 'Pinned' + visit project_path(project) + expect(page).to have_content 'Your pinned items appear here.' + end + end + end + describe 'pinned items' do before do within '#super-sidebar' do diff --git a/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap b/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap index b5a69b28a88..934bda570d4 100644 --- a/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap +++ b/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap @@ -7,7 +7,7 @@ exports[`Design management pagination component renders navigation buttons 1`] = class="gl-display-flex gl-align-items-center" > - 0 of 2 + 0 of 3 <gl-button-group-stub class="gl-mx-5" diff --git a/spec/frontend/design_management/components/toolbar/design_navigation_spec.js b/spec/frontend/design_management/components/toolbar/design_navigation_spec.js index 8427d83ceee..46eb4c16af8 100644 --- a/spec/frontend/design_management/components/toolbar/design_navigation_spec.js +++ b/spec/frontend/design_management/components/toolbar/design_navigation_spec.js @@ -1,9 +1,15 @@ /* global Mousetrap */ import 'mousetrap'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { GlButtonGroup } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; import DesignNavigation from '~/design_management/components/toolbar/design_navigation.vue'; import { DESIGN_ROUTE_NAME } from '~/design_management/router/constants'; +import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { designListQueryResponse, designListQueryResponseNodes } from '../../mock_data/apollo_mock'; const push = jest.fn(); const $router = { @@ -18,11 +24,24 @@ const $route = { describe('Design management pagination component', () => { let wrapper; - function createComponent() { + const buildMockHandler = (nodes = designListQueryResponseNodes) => { + designListQueryResponse.data.project.issue.designCollection.designs.nodes = nodes; + return jest.fn().mockResolvedValue(designListQueryResponse); + }; + + const createMockApolloProvider = (handler) => { + Vue.use(VueApollo); + + return createMockApollo([[getDesignListQuery, handler]]); + }; + + function createComponent({ propsData = {}, handler = buildMockHandler() } = {}) { wrapper = shallowMount(DesignNavigation, { propsData: { id: '2', + ...propsData, }, + apolloProvider: createMockApolloProvider(handler), mocks: { $router, $route, @@ -30,48 +49,45 @@ describe('Design management pagination component', () => { }); } - beforeEach(() => { - createComponent(); - }); + const findGlButtonGroup = () => wrapper.findComponent(GlButtonGroup); + + it('hides components when designs are empty', async () => { + createComponent({ handler: buildMockHandler([]) }); + await waitForPromises(); - it('hides components when designs are empty', () => { + expect(findGlButtonGroup().exists()).toBe(false); expect(wrapper.element).toMatchSnapshot(); }); it('renders navigation buttons', async () => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - designCollection: { designs: [{ id: '1' }, { id: '2' }] }, - }); + createComponent({ handler: buildMockHandler() }); + await waitForPromises(); - await nextTick(); + expect(findGlButtonGroup().exists()).toBe(true); expect(wrapper.element).toMatchSnapshot(); }); describe('keyboard buttons navigation', () => { - beforeEach(() => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - designCollection: { designs: [{ filename: '1' }, { filename: '2' }, { filename: '3' }] }, - }); - }); + it('routes to previous design on Left button', async () => { + createComponent({ propsData: { id: designListQueryResponseNodes[1].filename } }); + await waitForPromises(); - it('routes to previous design on Left button', () => { Mousetrap.trigger('left'); expect(push).toHaveBeenCalledWith({ name: DESIGN_ROUTE_NAME, - params: { id: '1' }, + params: { id: designListQueryResponseNodes[0].filename }, query: {}, }); }); - it('routes to next design on Right button', () => { + it('routes to next design on Right button', async () => { + createComponent({ propsData: { id: designListQueryResponseNodes[1].filename } }); + await waitForPromises(); + Mousetrap.trigger('right'); expect(push).toHaveBeenCalledWith({ name: DESIGN_ROUTE_NAME, - params: { id: '3' }, + params: { id: designListQueryResponseNodes[2].filename }, query: {}, }); }); diff --git a/spec/frontend/design_management/mock_data/apollo_mock.js b/spec/frontend/design_management/mock_data/apollo_mock.js index 2b99dcf14da..a7355719141 100644 --- a/spec/frontend/design_management/mock_data/apollo_mock.js +++ b/spec/frontend/design_management/mock_data/apollo_mock.js @@ -1,3 +1,45 @@ +export const designListQueryResponseNodes = [ + { + __typename: 'Design', + id: '1', + event: 'NONE', + filename: 'fox_1.jpg', + notesCount: 3, + image: 'image-1', + imageV432x230: 'image-1', + currentUserTodos: { + __typename: 'ToDo', + nodes: [], + }, + }, + { + __typename: 'Design', + id: '2', + event: 'NONE', + filename: 'fox_2.jpg', + notesCount: 2, + image: 'image-2', + imageV432x230: 'image-2', + currentUserTodos: { + __typename: 'ToDo', + nodes: [], + }, + }, + { + __typename: 'Design', + id: '3', + event: 'NONE', + filename: 'fox_3.jpg', + notesCount: 1, + image: 'image-3', + imageV432x230: 'image-3', + currentUserTodos: { + __typename: 'ToDo', + nodes: [], + }, + }, +]; + export const designListQueryResponse = { data: { project: { @@ -11,47 +53,7 @@ export const designListQueryResponse = { copyState: 'READY', designs: { __typename: 'DesignConnection', - nodes: [ - { - __typename: 'Design', - id: '1', - event: 'NONE', - filename: 'fox_1.jpg', - notesCount: 3, - image: 'image-1', - imageV432x230: 'image-1', - currentUserTodos: { - __typename: 'ToDo', - nodes: [], - }, - }, - { - __typename: 'Design', - id: '2', - event: 'NONE', - filename: 'fox_2.jpg', - notesCount: 2, - image: 'image-2', - imageV432x230: 'image-2', - currentUserTodos: { - __typename: 'ToDo', - nodes: [], - }, - }, - { - __typename: 'Design', - id: '3', - event: 'NONE', - filename: 'fox_3.jpg', - notesCount: 1, - image: 'image-3', - imageV432x230: 'image-3', - currentUserTodos: { - __typename: 'ToDo', - nodes: [], - }, - }, - ], + nodes: designListQueryResponseNodes, }, versions: { __typename: 'DesignVersion', diff --git a/spec/frontend/super_sidebar/components/pinned_section_spec.js b/spec/frontend/super_sidebar/components/pinned_section_spec.js new file mode 100644 index 00000000000..7ead6a40895 --- /dev/null +++ b/spec/frontend/super_sidebar/components/pinned_section_spec.js @@ -0,0 +1,75 @@ +import { nextTick } from 'vue'; +import Cookies from '~/lib/utils/cookies'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import PinnedSection from '~/super_sidebar/components/pinned_section.vue'; +import NavItem from '~/super_sidebar/components/nav_item.vue'; +import { SIDEBAR_PINS_EXPANDED_COOKIE, SIDEBAR_COOKIE_EXPIRATION } from '~/super_sidebar/constants'; +import { setCookie } from '~/lib/utils/common_utils'; + +jest.mock('~/lib/utils/common_utils', () => ({ + getCookie: jest.requireActual('~/lib/utils/common_utils').getCookie, + setCookie: jest.fn(), +})); + +describe('PinnedSection component', () => { + let wrapper; + + const findToggle = () => wrapper.find('a'); + + const createWrapper = () => { + wrapper = mountExtended(PinnedSection, { + propsData: { + items: [{ title: 'Pin 1', href: '/page1' }], + }, + }); + }; + + describe('expanded', () => { + describe('when cookie is not set', () => { + it('is expanded by default', () => { + createWrapper(); + expect(wrapper.findComponent(NavItem).isVisible()).toBe(true); + }); + }); + + describe('when cookie is set to false', () => { + beforeEach(() => { + Cookies.set(SIDEBAR_PINS_EXPANDED_COOKIE, 'false'); + createWrapper(); + }); + + it('is collapsed', () => { + expect(wrapper.findComponent(NavItem).isVisible()).toBe(false); + }); + + it('updates the cookie when expanding the section', async () => { + findToggle().trigger('click'); + await nextTick(); + + expect(setCookie).toHaveBeenCalledWith(SIDEBAR_PINS_EXPANDED_COOKIE, true, { + expires: SIDEBAR_COOKIE_EXPIRATION, + }); + }); + }); + + describe('when cookie is set to true', () => { + beforeEach(() => { + Cookies.set(SIDEBAR_PINS_EXPANDED_COOKIE, 'true'); + createWrapper(); + }); + + it('is expanded', () => { + expect(wrapper.findComponent(NavItem).isVisible()).toBe(true); + }); + + it('updates the cookie when collapsing the section', async () => { + findToggle().trigger('click'); + await nextTick(); + + expect(setCookie).toHaveBeenCalledWith(SIDEBAR_PINS_EXPANDED_COOKIE, false, { + expires: SIDEBAR_COOKIE_EXPIRATION, + }); + }); + }); + }); +}); diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 027c61bb9e1..78b83356675 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -403,63 +403,49 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor it_behaves_like 'requires authentication' - it 'returns NOT FOUND when the feature is disabled' do - stub_feature_flags(import_project_from_remote_file_s3: false) - - subject - - expect(response).to have_gitlab_http_status(:not_found) - end - - context 'when the feature flag is enabled' do - before do - stub_feature_flags(import_project_from_remote_file_s3: true) - end - - context 'when the response is successful' do - it 'schedules the import successfully' do - project = create( - :project, - namespace: user.namespace, - name: 'test-import', - path: 'test-import' - ) + context 'when the response is successful' do + it 'schedules the import successfully' do + project = create( + :project, + namespace: user.namespace, + name: 'test-import', + path: 'test-import' + ) - service_response = ServiceResponse.success(payload: project) - expect_next(::Import::GitlabProjects::CreateProjectService) - .to receive(:execute) - .and_return(service_response) + service_response = ServiceResponse.success(payload: project) + expect_next(::Import::GitlabProjects::CreateProjectService) + .to receive(:execute) + .and_return(service_response) - subject + subject - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to include({ - 'id' => project.id, - 'name' => 'test-import', - 'name_with_namespace' => "#{user.namespace.name} / test-import", - 'path' => 'test-import', - 'path_with_namespace' => "#{user.namespace.path}/test-import" - }) - end + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to include({ + 'id' => project.id, + 'name' => 'test-import', + 'name_with_namespace' => "#{user.namespace.name} / test-import", + 'path' => 'test-import', + 'path_with_namespace' => "#{user.namespace.path}/test-import" + }) end + end - context 'when the service returns an error' do - it 'fails to schedule the import' do - service_response = ServiceResponse.error( - message: 'Failed to import', - http_status: :bad_request - ) - expect_next(::Import::GitlabProjects::CreateProjectService) - .to receive(:execute) - .and_return(service_response) + context 'when the service returns an error' do + it 'fails to schedule the import' do + service_response = ServiceResponse.error( + message: 'Failed to import', + http_status: :bad_request + ) + expect_next(::Import::GitlabProjects::CreateProjectService) + .to receive(:execute) + .and_return(service_response) - subject + subject - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to eq({ - 'message' => 'Failed to import' - }) - end + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to eq({ + 'message' => 'Failed to import' + }) end end end diff --git a/workhorse/Makefile b/workhorse/Makefile index ba99276d1be..ac6c5456340 100644 --- a/workhorse/Makefile +++ b/workhorse/Makefile @@ -29,8 +29,7 @@ ifeq (${FIPS_MODE}, 1) export CGO_ENABLED=1 # Go 1.19+ now requires GOEXPERIMENT=boringcrypto for FIPS compilation. # See https://github.com/golang/go/issues/51940 for more details. - BORINGCRYPTO_SUPPORT := $(shell GOEXPERIMENT=boringcrypto go version &> /dev/null; echo $$?) - ifeq ($(BORINGCRYPTO_SUPPORT), 0) + ifeq ($(shell GOEXPERIMENT=boringcrypto go version > /dev/null 2>&1; echo $$?), 0) export GOEXPERIMENT=boringcrypto endif endif |