diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-24 09:09:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-24 09:09:49 +0300 |
commit | 0d312b8d370738d1dfa089b59ed21f40af1d4add (patch) | |
tree | 1087b58a57d0ccfea0c14b7c89e8c8513138819c | |
parent | 2a0815d5fc681408f69f13fb33d9f0d87590fa45 (diff) |
Add latest changes from gitlab-org/gitlab@master
29 files changed, 224 insertions, 222 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js index 6b7b0c2e28d..0142c95e773 100644 --- a/app/assets/javascripts/boards/boards_util.js +++ b/app/assets/javascripts/boards/boards_util.js @@ -1,5 +1,4 @@ import { sortBy } from 'lodash'; -import ListIssue from 'ee_else_ce/boards/models/issue'; import { ListType } from './constants'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import boardsStore from '~/boards/stores/boards_store'; @@ -21,11 +20,11 @@ export function formatBoardLists(lists) { } export function formatIssue(issue) { - return new ListIssue({ + return { ...issue, labels: issue.labels?.nodes || [], assignees: issue.assignees?.nodes || [], - }); + }; } export function formatListIssues(listIssues) { @@ -44,12 +43,12 @@ export function formatListIssues(listIssues) { [list.id]: sortedIssues.map(i => { const id = getIdFromGraphQLId(i.id); - const listIssue = new ListIssue({ + const listIssue = { ...i, id, labels: i.labels?.nodes || [], assignees: i.assignees?.nodes || [], - }); + }; issues[id] = listIssue; @@ -83,21 +82,30 @@ export function fullLabelId(label) { } export function moveIssueListHelper(issue, fromList, toList) { - if (toList.type === ListType.label) { - issue.addLabel(toList.label); + const updatedIssue = issue; + if ( + toList.type === ListType.label && + !updatedIssue.labels.find(label => label.id === toList.label.id) + ) { + updatedIssue.labels.push(toList.label); } - if (fromList && fromList.type === ListType.label) { - issue.removeLabel(fromList.label); + if (fromList?.label && fromList.type === ListType.label) { + updatedIssue.labels = updatedIssue.labels.filter(label => fromList.label.id !== label.id); } - if (toList.type === ListType.assignee) { - issue.addAssignee(toList.assignee); + if ( + toList.type === ListType.assignee && + !updatedIssue.assignees.find(assignee => assignee.id === toList.assignee.id) + ) { + updatedIssue.assignees.push(toList.assignee); } - if (fromList && fromList.type === ListType.assignee) { - issue.removeAssignee(fromList.assignee); + if (fromList?.assignee && fromList.type === ListType.assignee) { + updatedIssue.assignees = updatedIssue.assignees.filter( + assignee => assignee.id !== fromList.assignee.id, + ); } - return issue; + return updatedIssue; } export default { diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 45ce1e51489..67e8a32dbe2 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -158,9 +158,13 @@ export default { class="confidential-icon gl-mr-2" :aria-label="__('Confidential')" /> - <a :href="issue.path" :title="issue.title" class="js-no-trigger" @mousemove.stop>{{ - issue.title - }}</a> + <a + :href="issue.path || issue.webUrl || ''" + :title="issue.title" + class="js-no-trigger" + @mousemove.stop + >{{ issue.title }}</a + > </h4> </div> <div v-if="showLabelFooter" class="board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap"> @@ -196,7 +200,11 @@ export default { #{{ issue.iid }} </span> <span class="board-info-items gl-mt-3 gl-display-inline-block"> - <issue-due-date v-if="issue.dueDate" :date="issue.dueDate" :closed="issue.closed" /> + <issue-due-date + v-if="issue.dueDate" + :date="issue.dueDate" + :closed="issue.closed || Boolean(issue.closedAt)" + /> <issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" /> <issue-card-weight v-if="validIssueWeight" diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index d96c245d912..8b43c7238fd 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -166,6 +166,13 @@ export const getRawFileData = ({ state, commit, dispatch, getters }, { path }) = export const changeFileContent = ({ commit, state, getters }, { path, content }) => { const file = state.entries[path]; + + // It's possible for monaco to hit a race condition where it tries to update renamed files. + // See issue https://gitlab.com/gitlab-org/gitlab/-/issues/284930 + if (!file) { + return; + } + commit(types.UPDATE_FILE_CONTENT, { path, content, diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js index 477a1ab887b..19aeb1d1ecf 100644 --- a/app/assets/javascripts/pages/projects/new/index.js +++ b/app/assets/javascripts/pages/projects/new/index.js @@ -2,46 +2,28 @@ import initProjectVisibilitySelector from '../../../project_visibility'; import initProjectNew from '../../../projects/project_new'; import { __ } from '~/locale'; import { deprecatedCreateFlash as createFlash } from '~/flash'; -import Tracking from '~/tracking'; -import { isExperimentEnabled } from '~/lib/utils/experimentation'; document.addEventListener('DOMContentLoaded', () => { initProjectVisibilitySelector(); initProjectNew.bindEvents(); - const { category, property } = gon.tracking_data ?? { category: 'projects:new' }; - const hasNewCreateProjectUi = isExperimentEnabled('newCreateProjectUi'); + import( + /* webpackChunkName: 'experiment_new_project_creation' */ '../../../projects/experiment_new_project_creation' + ) + .then(m => { + const el = document.querySelector('.js-experiment-new-project-creation'); - if (!hasNewCreateProjectUi) { - // Setting additional tracking for HAML template + if (!el) { + return; + } - Array.from( - document.querySelectorAll('.project-edit-container [data-experiment-track-label]'), - ).forEach(node => - node.addEventListener('click', event => { - const { experimentTrackLabel: label } = event.currentTarget.dataset; - Tracking.event(category, 'click_tab', { property, label }); - }), - ); - } else { - import( - /* webpackChunkName: 'experiment_new_project_creation' */ '../../../projects/experiment_new_project_creation' - ) - .then(m => { - const el = document.querySelector('.js-experiment-new-project-creation'); - - if (!el) { - return; - } - - const config = { - hasErrors: 'hasErrors' in el.dataset, - isCiCdAvailable: 'isCiCdAvailable' in el.dataset, - }; - m.default(el, config); - }) - .catch(() => { - createFlash(__('An error occurred while loading project creation UI')); - }); - } + const config = { + hasErrors: 'hasErrors' in el.dataset, + isCiCdAvailable: 'isCiCdAvailable' in el.dataset, + }; + m.default(el, config); + }) + .catch(() => { + createFlash(__('An error occurred while loading project creation UI')); + }); }); diff --git a/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue b/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue index f404e6030f4..2e16071e563 100644 --- a/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue +++ b/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue @@ -12,6 +12,7 @@ import ciCdProjectIllustration from '../illustrations/ci-cd-project.svg'; const BLANK_PANEL = 'blank_project'; const CI_CD_PANEL = 'cicd_for_external_repo'; +const LAST_ACTIVE_TAB_KEY = 'new_project_last_active_tab'; const PANELS = [ { name: BLANK_PANEL, @@ -105,7 +106,7 @@ export default { this.handleLocationHashChange(); if (this.hasErrors) { - this.activeTab = BLANK_PANEL; + this.activeTab = localStorage.getItem(LAST_ACTIVE_TAB_KEY) || BLANK_PANEL; } window.addEventListener('hashchange', () => { @@ -127,6 +128,9 @@ export default { handleLocationHashChange() { this.activeTab = window.location.hash.substring(1) || null; + if (this.activeTab) { + localStorage.setItem(LAST_ACTIVE_TAB_KEY, this.activeTab); + } }, }, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue index 82566682bca..bc23ca6b1fc 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue @@ -1,10 +1,11 @@ <script> -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import ciIcon from '../../vue_shared/components/ci_icon.vue'; export default { components: { ciIcon, + GlButton, GlLoadingIcon, }, props: { @@ -32,21 +33,23 @@ export default { }; </script> <template> - <div class="d-flex align-self-start"> + <div class="gl-display-flex gl-align-self-start"> <div class="square s24 h-auto d-flex-center gl-mr-3"> - <div v-if="isLoading" class="mr-widget-icon d-inline-flex"> - <gl-loading-icon size="md" class="mr-loading-icon d-inline-flex" /> + <div v-if="isLoading" class="mr-widget-icon gl-display-inline-flex"> + <gl-loading-icon size="md" class="mr-loading-icon gl-display-inline-flex" /> </div> <ci-icon v-else :status="statusObj" :size="24" /> </div> - <button + <gl-button v-if="showDisabledButton" type="button" - class="js-disabled-merge-button btn btn-success btn-sm" - disabled="true" + category="primary" + variant="success" + class="js-disabled-merge-button" + :disabled="true" > {{ s__('mrWidget|Merge') }} - </button> + </gl-button> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue index d421b744fa1..2df03fbc679 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue @@ -1,6 +1,7 @@ <script> import $ from 'jquery'; import { escape } from 'lodash'; +import { GlButton, GlModalDirective } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover'; import StatusIcon from '../mr_widget_status_icon.vue'; @@ -9,6 +10,10 @@ export default { name: 'MRWidgetConflicts', components: { StatusIcon, + GlButton, + }, + directives: { + GlModalDirective, }, props: { /* TODO: This is providing all store and service down when it @@ -89,22 +94,21 @@ To merge this request, first rebase locally.`) </span> </span> <span v-if="showResolveButton" ref="popover"> - <a + <gl-button :href="mr.conflictResolutionPath" :disabled="mr.sourceBranchProtected" - class="js-resolve-conflicts-button btn btn-default btn-sm" + class="js-resolve-conflicts-button" > {{ s__('mrWidget|Resolve conflicts') }} - </a> + </gl-button> </span> - <button + <gl-button v-if="mr.canMerge" - class="js-merge-locally-button btn btn-default btn-sm" - data-toggle="modal" - data-target="#modal_merge_info" + v-gl-modal-directive="'modal-merge-info'" + class="js-merge-locally-button" > {{ s__('mrWidget|Merge locally') }} - </button> + </gl-button> </template> </div> </div> diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c03a820b384..8666b0f9576 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -34,12 +34,6 @@ class ProjectsController < Projects::ApplicationController # Project Export Rate Limit before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export] - # Experiments - before_action only: [:new, :create] do - frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab') - push_frontend_experiment(:new_create_project_ui) - end - before_action only: [:edit] do push_frontend_feature_flag(:service_desk_custom_address, @project) push_frontend_feature_flag(:approval_suggestions, @project, default_enabled: true) diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index f2972a9617b..a407aa9ac13 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -8,10 +8,9 @@ .project-edit-errors = render 'projects/errors' - - if experiment_enabled?(:new_create_project_ui) - .js-experiment-new-project-creation{ data: { is_ci_cd_available: ci_cd_projects_available?, has_errors: @project.errors.any? } } + .js-experiment-new-project-creation{ data: { is_ci_cd_available: (ci_cd_projects_available? if Gitlab.ee?), has_errors: @project.errors.any? } } - .row{ 'v-cloak': experiment_enabled?(:new_create_project_ui) } + .row{ 'v-cloak': true } .col-lg-3.profile-settings-sidebar %h4.gl-mt-0 = _('New project') diff --git a/changelogs/unreleased/229677-mr-conflicts.yml b/changelogs/unreleased/229677-mr-conflicts.yml new file mode 100644 index 00000000000..f8d104eafee --- /dev/null +++ b/changelogs/unreleased/229677-mr-conflicts.yml @@ -0,0 +1,5 @@ +--- +title: Make How to merge modal in merge requests widget conform to correct modal styling +merge_request: 48370 +author: +type: changed diff --git a/changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml b/changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml new file mode 100644 index 00000000000..8194806f9c0 --- /dev/null +++ b/changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml @@ -0,0 +1,5 @@ +--- +title: Fix console error being thrown when file is renamed +merge_request: 48275 +author: +type: fixed diff --git a/changelogs/unreleased/dennis-update-new-project-ui-experiment.yml b/changelogs/unreleased/dennis-update-new-project-ui-experiment.yml new file mode 100644 index 00000000000..6c660a30e46 --- /dev/null +++ b/changelogs/unreleased/dennis-update-new-project-ui-experiment.yml @@ -0,0 +1,5 @@ +--- +title: Finalize new create project UI experiment +merge_request: 47804 +author: +type: changed diff --git a/doc/integration/img/sourcegraph_admin_v12_5.png b/doc/integration/img/sourcegraph_admin_v12_5.png Binary files differindex 54511541c87..e42371a681e 100644 --- a/doc/integration/img/sourcegraph_admin_v12_5.png +++ b/doc/integration/img/sourcegraph_admin_v12_5.png diff --git a/doc/integration/sourcegraph.md b/doc/integration/sourcegraph.md index 47c84643a7d..4bbb34b5fda 100644 --- a/doc/integration/sourcegraph.md +++ b/doc/integration/sourcegraph.md @@ -64,6 +64,8 @@ Feature.enable(:sourcegraph, Project.find_by_full_path('my_group/my_project')) If you are new to Sourcegraph, head over to the [Sourcegraph installation documentation](https://docs.sourcegraph.com/admin) and get your instance up and running. +If you are using an HTTPS connection to GitLab, you will need to [configure HTTPS](https://docs.sourcegraph.com/admin/http_https_configuration) for your Sourcegraph instance. + ### Connect your Sourcegraph instance to your GitLab instance 1. Navigate to the site admin area in Sourcegraph. diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index 6c17568c617..740856f84c9 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -554,9 +554,9 @@ DAST can be [configured](#customizing-the-dast-settings) using environment varia | `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | | `DAST_USE_AJAX_SPIDER` | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | | `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in GitLab 13.4. | -| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line and be within `/zap/wrk`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. | -| `DAST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when clicked will submit the login form or the password form of a multi-page login process. | -| `DAST_FIRST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when clicked will submit the username form of a multi-page login process. | +| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line and be in `/zap/wrk`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. | +| `DAST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when clicked submits the login form or the password form of a multi-page login process. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. | +| `DAST_FIRST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when clicked submits the username form of a multi-page login process. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. | | `DAST_ZAP_CLI_OPTIONS` | string | ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. | | `DAST_ZAP_LOG_CONFIGURATION` | string | Set to a semicolon-separated list of additional log4j properties for the ZAP Server. For example, `log4j.logger.org.parosproxy.paros.network.HttpSender=DEBUG;log4j.logger.com.crawljax=DEBUG` | diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 8ae92a42581..37623c94eda 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -188,15 +188,12 @@ To set your current status: 1. Set the desired emoji and/or status message. 1. Click **Set status**. Alternatively, you can click **Remove status** to remove your user status entirely. -![Busy status indicator](img/busy_status_indicator_v13_6.png) - or 1. Click your avatar. 1. Select **Profile**. 1. Click **Edit profile** (pencil icon). 1. Enter your status message in the **Your status** text field. - 1. Alternatively, select the **Busy** checkbox ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259649) in GitLab 13.6}. 1. Click **Add status emoji** (smiley face), and select the desired emoji. 1. Click **Update profile settings**. @@ -204,6 +201,44 @@ You can also set your current status [using the API](../../api/users.md#user-sta If you previously selected the "Busy" checkbox, remember to deselect it when you become available again. +## Busy status indicator + +> - Introduced in GitLab 13.6. +> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. +> - It's disabled on GitLab.com. +> - It's not recommended for production use. +> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-busy-status-feature). + +To indicate to others that you are busy, you can set an indicator + +![Busy status indicator](img/busy_status_indicator_v13_6.png) + +To set the busy status indicator, either: + +- Set it directly: + + 1. Click your avatar. + 1. Click **Set status**, or **Edit status** if you have already set a status. + 1. Select the **Busy** checkbox + +- Set it on your profile: + + 1. Click your avatar. + 1. Select **Profile**. + 1. Click **Edit profile** (**{pencil}**). + 1. Select the **Busy** checkbox + +### Enable busy status feature + +The busy status feature is deployed behind a feature flag and is **disabled by default**. +[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) can enable it for your instance from the [rails console](../../administration/feature_flags.md#start-the-gitlab-rails-console). + +To enable it: + +```ruby +Feature.enable(:set_user_availability_status) +``` + ## Commit email > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/21598) in GitLab 11.4. diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb index d442e103300..d511972757f 100644 --- a/lib/gitlab/experimentation.rb +++ b/lib/gitlab/experimentation.rb @@ -54,10 +54,6 @@ module Gitlab tracking_category: 'Growth::Expansion::Experiment::InviteMembersEmptyGroupVersionA', use_backwards_compatible_subject_index: true }, - new_create_project_ui: { - tracking_category: 'Manage::Import::Experiment::NewCreateProjectUi', - use_backwards_compatible_subject_index: true - }, contact_sales_btn_in_app: { tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp', use_backwards_compatible_subject_index: true diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 012a98f433e..ced590039e1 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -41,27 +41,6 @@ RSpec.describe ProjectsController do end end end - - context 'with the new_create_project_ui experiment enabled and the user is part of the control group' do - before do - stub_experiment(new_create_project_ui: true) - stub_experiment_for_user(new_create_project_ui: false) - allow_any_instance_of(described_class).to receive(:experimentation_subject_id).and_return('uuid') - end - - it 'passes the right tracking parameters to the frontend' do - get(:new) - - expect(Gon.tracking_data).to eq( - { - category: 'Manage::Import::Experiment::NewCreateProjectUi', - action: 'click_tab', - label: 'uuid', - property: 'control_group' - } - ) - end - end end end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 83ceffa621c..af228764c17 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -28,73 +28,40 @@ RSpec.describe 'Import/Export - project import integration test', :js do let(:project_name) { 'Test Project Name' + randomHex } let(:project_path) { 'test-project-name' + randomHex } - context 'prefilled the path' do - it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do - visit new_project_path + it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do + visit new_project_path + click_import_project_tab + click_link 'GitLab export' - fill_in :project_name, with: project_name, visible: true - click_import_project_tab - click_link 'GitLab export' + fill_in :name, with: 'Test Project Name', visible: true + fill_in :path, with: 'test-project-path', visible: true + attach_file('file', file) - expect(page).to have_content('Import an exported GitLab project') - expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&name=#{ERB::Util.url_encode(project_name)}&path=#{project_path}") + expect { click_on 'Import project' }.to change { Project.count }.by(1) - attach_file('file', file) - click_on 'Import project' - - expect(Project.count).to eq(1) - - project = Project.last - expect(project).not_to be_nil - expect(project.description).to eq("Foo Bar") - expect(project.issues).not_to be_empty - expect(project.merge_requests).not_to be_empty - expect(wiki_exists?(project)).to be true - expect(project.import_state.status).to eq('finished') - end + project = Project.last + expect(project).not_to be_nil + expect(page).to have_content("Project 'test-project-path' is being imported") end - context 'path is not prefilled' do - it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do - visit new_project_path - click_import_project_tab - click_link 'GitLab export' + it 'invalid project' do + project = create(:project, namespace: user.namespace) - fill_in :name, with: 'Test Project Name', visible: true - fill_in :path, with: 'test-project-path', visible: true - attach_file('file', file) + visit new_project_path - expect { click_on 'Import project' }.to change { Project.count }.by(1) + click_import_project_tab + click_link 'GitLab export' + fill_in :name, with: project.name, visible: true + attach_file('file', file) + click_on 'Import project' - project = Project.last - expect(project).not_to be_nil - expect(page).to have_content("Project 'test-project-path' is being imported") + page.within('.flash-container') do + expect(page).to have_content('Project could not be imported') end end end - it 'invalid project' do - project = create(:project, namespace: user.namespace) - - visit new_project_path - - fill_in :project_name, with: project.name, visible: true - click_import_project_tab - click_link 'GitLab export' - attach_file('file', file) - click_on 'Import project' - - page.within('.flash-container') do - expect(page).to have_content('Project could not be imported') - end - end - - def wiki_exists?(project) - wiki = ProjectWiki.new(project) - wiki.repository.exists? && !wiki.repository.empty? - end - def click_import_project_tab - find('#import-project-tab').click + find('[data-qa-selector="import_project_link"]').click end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 6a2ec9aa4a8..796fd76cfdf 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'New project' do +RSpec.describe 'New project', :js do include Select2Helper context 'as a user' do @@ -18,6 +18,7 @@ RSpec.describe 'New project' do ) visit new_project_path + find('[data-qa-selector="blank_project_link"]').click expect(page).to have_content 'Other visibility settings have been disabled by the administrator.' end @@ -28,6 +29,7 @@ RSpec.describe 'New project' do ) visit new_project_path + find('[data-qa-selector="blank_project_link"]').click expect(page).to have_content 'Visibility settings have been disabled by the administrator.' end @@ -42,12 +44,14 @@ RSpec.describe 'New project' do it 'shows "New project" page', :js do visit new_project_path + find('[data-qa-selector="blank_project_link"]').click expect(page).to have_content('Project name') expect(page).to have_content('Project URL') expect(page).to have_content('Project slug') - find('#import-project-tab').click + click_link('New project') + find('[data-qa-selector="import_project_link"]').click expect(page).to have_link('GitHub') expect(page).to have_link('Bitbucket') @@ -61,7 +65,7 @@ RSpec.describe 'New project' do before do visit new_project_path - find('#import-project-tab').click + find('[data-qa-selector="import_project_link"]').click end it { expect(page).to have_link('Manifest file') } @@ -73,6 +77,7 @@ RSpec.describe 'New project' do stub_application_setting(default_project_visibility: level) visit new_project_path + find('[data-qa-selector="blank_project_link"]').click page.within('#blank-project-pane') do expect(find_field("project_visibility_level_#{level}")).to be_checked end @@ -80,6 +85,7 @@ RSpec.describe 'New project' do it "saves visibility level #{level} on validation error" do visit new_project_path + find('[data-qa-selector="blank_project_link"]').click choose(s_(key)) click_button('Create project') @@ -97,6 +103,7 @@ RSpec.describe 'New project' do it 'has private selected' do group = create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) visit new_project_path(namespace_id: group.id) + find('[data-qa-selector="blank_project_link"]').click page.within('#blank-project-pane') do expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked @@ -112,6 +119,7 @@ RSpec.describe 'New project' do it 'has private selected' do group = create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) + find('[data-qa-selector="blank_project_link"]').click page.within('#blank-project-pane') do expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked @@ -123,6 +131,7 @@ RSpec.describe 'New project' do context 'Readme selector' do it 'shows the initialize with Readme checkbox on "Blank project" tab' do visit new_project_path + find('[data-qa-selector="blank_project_link"]').click expect(page).to have_css('input#project_initialize_with_readme') expect(page).to have_content('Initialize repository with a README') @@ -130,7 +139,7 @@ RSpec.describe 'New project' do it 'does not show the initialize with Readme checkbox on "Create from template" tab' do visit new_project_path - find('#create-from-template-pane').click + find('[data-qa-selector="create_from_template_link"]').click first('.choose-template').click page.within '.project-fields-form' do @@ -141,7 +150,7 @@ RSpec.describe 'New project' do it 'does not show the initialize with Readme checkbox on "Import project" tab' do visit new_project_path - find('#import-project-tab').click + find('[data-qa-selector="import_project_link"]').click first('.js-import-git-toggle-button').click page.within '.toggle-import-form' do @@ -155,13 +164,12 @@ RSpec.describe 'New project' do context 'with user namespace' do before do visit new_project_path + find('[data-qa-selector="blank_project_link"]').click end it 'selects the user namespace' do page.within('#blank-project-pane') do - namespace = find('#project_namespace_id') - - expect(namespace.text).to eq user.username + expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username) end end end @@ -172,13 +180,12 @@ RSpec.describe 'New project' do before do group.add_owner(user) visit new_project_path(namespace_id: group.id) + find('[data-qa-selector="blank_project_link"]').click end it 'selects the group namespace' do page.within('#blank-project-pane') do - namespace = find('#project_namespace_id option[selected]') - - expect(namespace.text).to eq group.name + expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name) end end end @@ -190,13 +197,12 @@ RSpec.describe 'New project' do before do group.add_maintainer(user) visit new_project_path(namespace_id: subgroup.id) + find('[data-qa-selector="blank_project_link"]').click end it 'selects the group namespace' do page.within('#blank-project-pane') do - namespace = find('#project_namespace_id option[selected]') - - expect(namespace.text).to eq subgroup.full_path + expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path) end end end @@ -211,6 +217,7 @@ RSpec.describe 'New project' do internal_group.add_owner(user) private_group.add_owner(user) visit new_project_path(namespace_id: public_group.id) + find('[data-qa-selector="blank_project_link"]').click end it 'enables the correct visibility options' do @@ -240,7 +247,7 @@ RSpec.describe 'New project' do context 'Import project options', :js do before do visit new_project_path - find('#import-project-tab').click + find('[data-qa-selector="import_project_link"]').click end context 'from git repository url, "Repo by URL"' do @@ -315,13 +322,12 @@ RSpec.describe 'New project' do before do group.add_developer(user) visit new_project_path(namespace_id: group.id) + find('[data-qa-selector="blank_project_link"]').click end it 'selects the group namespace' do page.within('#blank-project-pane') do - namespace = find('#project_namespace_id option[selected]') - - expect(namespace.text).to eq group.full_path + expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path) end end end diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index b204ae76e07..feb5f348256 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -13,6 +13,7 @@ RSpec.describe 'User creates a project', :js do it 'creates a new project' do visit(new_project_path) + find('[data-qa-selector="blank_project_link"]').click fill_in(:project_name, with: 'Empty') page.within('#content-body') do @@ -39,6 +40,7 @@ RSpec.describe 'User creates a project', :js do it 'creates a new project' do visit(new_project_path) + find('[data-qa-selector="blank_project_link"]').click fill_in :project_name, with: 'A Subgroup Project' fill_in :project_path, with: 'a-subgroup-project' @@ -67,6 +69,7 @@ RSpec.describe 'User creates a project', :js do it 'creates a new project' do visit(new_project_path) + find('[data-qa-selector="blank_project_link"]').click fill_in :project_name, with: 'a-new-project' fill_in :project_path, with: 'a-new-project' diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index d88e948626d..e67fb9c2bd6 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Project' do shared_examples 'creates from template' do |template, sub_template_tab = nil| it "is created from template", :js do - find('#create-from-template-tab').click + find('[data-qa-selector="create_from_template_link"]').click find(".project-template #{sub_template_tab}").click if sub_template_tab find("label[for=#{template.name}]").click fill_in("project_name", with: template.name) @@ -47,9 +47,7 @@ RSpec.describe 'Project' do end it 'shows the command in a popover', :js do - page.within '.profile-settings-sidebar' do - click_link 'Show command' - end + click_link 'Show command' expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip') expect(page).to have_content 'Private projects can be created in your personal namespace with:' diff --git a/spec/frontend/boards/board_list_new_spec.js b/spec/frontend/boards/board_list_new_spec.js index 17dc25c2782..585c0f37329 100644 --- a/spec/frontend/boards/board_list_new_spec.js +++ b/spec/frontend/boards/board_list_new_spec.js @@ -1,5 +1,4 @@ /* global List */ -/* global ListIssue */ import Vuex from 'vuex'; import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame'; @@ -7,7 +6,6 @@ import { createLocalVue, mount } from '@vue/test-utils'; import eventHub from '~/boards/eventhub'; import BoardList from '~/boards/components/board_list_new.vue'; import BoardCard from '~/boards/components/board_card.vue'; -import '~/boards/models/issue'; import '~/boards/models/list'; import { listObj, mockIssuesByListId, issues, mockIssues } from './mock_data'; import defaultState from '~/boards/stores/state'; @@ -52,7 +50,7 @@ const createComponent = ({ ...listProps, doNotFetchIssues: true, }); - const issue = new ListIssue({ + const issue = { title: 'Testing', id: 1, iid: 1, @@ -60,7 +58,7 @@ const createComponent = ({ labels: [], assignees: [], ...listIssueProps, - }); + }; if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesSize')) { list.issuesSize = 1; } diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js index 58f67231d55..a81db9c8404 100644 --- a/spec/frontend/boards/mock_data.js +++ b/spec/frontend/boards/mock_data.js @@ -1,10 +1,8 @@ -/* global ListIssue */ /* global List */ import Vue from 'vue'; import { keyBy } from 'lodash'; import '~/boards/models/list'; -import '~/boards/models/issue'; import boardsStore from '~/boards/stores/boards_store'; export const boardObj = { @@ -184,8 +182,6 @@ export const mockActiveIssue = { emailsDisabled: false, }; -export const mockIssueWithModel = new ListIssue(mockIssue); - export const mockIssue2 = { id: 'gid://gitlab/Issue/437', iid: 28, @@ -203,8 +199,6 @@ export const mockIssue2 = { }, }; -export const mockIssue2WithModel = new ListIssue(mockIssue2); - export const mockIssue3 = { id: 'gid://gitlab/Issue/438', iid: 29, diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index 1e59951ea66..f168e427431 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -4,8 +4,7 @@ import { mockLists, mockListsById, mockIssue, - mockIssueWithModel, - mockIssue2WithModel, + mockIssue2, rawIssue, mockIssues, mockMilestone, @@ -500,8 +499,8 @@ describe('moveIssue', () => { }; const issues = { - '436': mockIssueWithModel, - '437': mockIssue2WithModel, + '436': mockIssue, + '437': mockIssue2, }; const state = { @@ -537,7 +536,7 @@ describe('moveIssue', () => { { type: types.MOVE_ISSUE, payload: { - originalIssue: mockIssueWithModel, + originalIssue: mockIssue, fromListId: 'gid://gitlab/List/1', toListId: 'gid://gitlab/List/2', }, @@ -612,7 +611,7 @@ describe('moveIssue', () => { { type: types.MOVE_ISSUE, payload: { - originalIssue: mockIssueWithModel, + originalIssue: mockIssue, fromListId: 'gid://gitlab/List/1', toListId: 'gid://gitlab/List/2', }, @@ -620,7 +619,7 @@ describe('moveIssue', () => { { type: types.MOVE_ISSUE_FAILURE, payload: { - originalIssue: mockIssueWithModel, + originalIssue: mockIssue, fromListId: 'gid://gitlab/List/1', toListId: 'gid://gitlab/List/2', originalIndex: 0, diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js index e1e57a8fd43..65b37007b5c 100644 --- a/spec/frontend/boards/stores/mutations_spec.js +++ b/spec/frontend/boards/stores/mutations_spec.js @@ -1,15 +1,7 @@ import mutations from '~/boards/stores/mutations'; import * as types from '~/boards/stores/mutation_types'; import defaultState from '~/boards/stores/state'; -import { - mockListsWithModel, - mockLists, - rawIssue, - mockIssue, - mockIssue2, - mockIssueWithModel, - mockIssue2WithModel, -} from '../mock_data'; +import { mockListsWithModel, mockLists, rawIssue, mockIssue, mockIssue2 } from '../mock_data'; const expectNotImplemented = action => { it('is not implemented', () => { @@ -355,8 +347,8 @@ describe('Board Store Mutations', () => { }; const issues = { - '1': mockIssueWithModel, - '2': mockIssue2WithModel, + '1': mockIssue, + '2': mockIssue2, }; state = { @@ -367,7 +359,7 @@ describe('Board Store Mutations', () => { }; mutations.MOVE_ISSUE(state, { - originalIssue: mockIssue2WithModel, + originalIssue: mockIssue2, fromListId: 'gid://gitlab/List/1', toListId: 'gid://gitlab/List/2', }); @@ -396,7 +388,7 @@ describe('Board Store Mutations', () => { issue: rawIssue, }); - expect(state.issues).toEqual({ '436': { ...mockIssueWithModel, id: 436 } }); + expect(state.issues).toEqual({ '436': { ...mockIssue, id: 436 } }); }); }); diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js index cc290fc526e..744ac086b5f 100644 --- a/spec/frontend/ide/stores/actions/file_spec.js +++ b/spec/frontend/ide/stores/actions/file_spec.js @@ -510,8 +510,6 @@ describe('IDE store file actions', () => { describe('changeFileContent', () => { let tmpFile; - const callAction = (content = 'content\n') => - store.dispatch('changeFileContent', { path: tmpFile.path, content }); beforeEach(() => { tmpFile = file('tmpFile'); @@ -521,11 +519,23 @@ describe('IDE store file actions', () => { }); it('updates file content', () => { - return callAction().then(() => { + const content = 'content\n'; + + return store.dispatch('changeFileContent', { path: tmpFile.path, content }).then(() => { expect(tmpFile.content).toBe('content\n'); }); }); + it('does nothing if path does not exist', () => { + const content = 'content\n'; + + return store + .dispatch('changeFileContent', { path: 'not/a/real_file.txt', content }) + .then(() => { + expect(tmpFile.content).toBe('\n'); + }); + }); + it('adds file into stagedFiles array', () => { return store .dispatch('changeFileContent', { diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js index 19f8a67d066..b8cd1469179 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js @@ -194,7 +194,7 @@ describe('MRWidgetConflicts', () => { }); it('sets resolve button as disabled', () => { - expect(vm.find('.js-resolve-conflicts-button').attributes('disabled')).toBe('disabled'); + expect(vm.find('.js-resolve-conflicts-button').attributes('disabled')).toBe('true'); }); it('renders popover', () => { diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb index 1e3ec1d11f5..4130d5f9184 100644 --- a/spec/lib/gitlab/experimentation_spec.rb +++ b/spec/lib/gitlab/experimentation_spec.rb @@ -13,7 +13,6 @@ RSpec.describe Gitlab::Experimentation::EXPERIMENTS do :invite_members_version_a, :invite_members_version_b, :invite_members_empty_group_version_a, - :new_create_project_ui, :contact_sales_btn_in_app, :customize_homepage, :invite_email, |