From 42883e12f1124f3bc3e99794b4b03c4a5dba1017 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 30 Oct 2020 00:09:00 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITALY_SERVER_VERSION | 2 +- .../sidebar/board_sidebar_due_date.vue | 115 +++++++++++++++ .../issue_set_due_date.mutation.graphql | 8 + .../javascripts/boards/stores/actions.js | 25 ++++ .../application_settings/_signup.html.haml | 3 + .../application_settings/network.html.haml | 4 + ...60_remove_sse_erb_support_feature_flag.yml | 5 + .../unreleased/vs-admin-new-user-signups.yml | 5 + .../resource_access_token_feature.yml} | 4 +- .../development/sse_erb_support.yml | 7 - config/initializers/0_inject_feature_flags.rb | 1 - ...ser_signups_cap_to_application_settings.rb | 9 ++ db/schema_migrations/20201019113009 | 1 + db/structure.sql | 1 + doc/.vale/gitlab/spelling-exceptions.txt | 1 + .../operations/extra_sidekiq_processes.md | 6 +- doc/development/graphql_guide/pagination.md | 123 +++++++++++++++- .../config/generated_config.rb | 4 +- locale/gitlab.pot | 12 ++ scripts/review_apps/base-config.yaml | 4 +- .../sidebar/board_sidebar_due_date_spec.js | 137 ++++++++++++++++++ spec/frontend/boards/stores/actions_spec.js | 51 +++++++ .../config/generated_config_spec.rb | 20 +-- 23 files changed, 510 insertions(+), 38 deletions(-) create mode 100644 app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue create mode 100644 app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql create mode 100644 changelogs/unreleased/235460_remove_sse_erb_support_feature_flag.yml create mode 100644 changelogs/unreleased/vs-admin-new-user-signups.yml rename config/feature_flags/{licensed/resource_access_token.yml => development/resource_access_token_feature.yml} (78%) delete mode 100644 config/feature_flags/development/sse_erb_support.yml create mode 100644 db/migrate/20201019113009_add_new_user_signups_cap_to_application_settings.rb create mode 100644 db/schema_migrations/20201019113009 create mode 100644 spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index ad120a28f84..ba5a2b86773 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -092d4e489d7de1dcc38d57a4c667e85df8b8377f +6aabab39e06f8a4f786a99449a49d5f0cb332310 diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue new file mode 100644 index 00000000000..97fe7572493 --- /dev/null +++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue @@ -0,0 +1,115 @@ + + + + diff --git a/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql b/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql new file mode 100644 index 00000000000..bbea248cf85 --- /dev/null +++ b/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql @@ -0,0 +1,8 @@ +mutation issueSetDueDate($input: UpdateIssueInput!) { + updateIssue(input: $input) { + issue { + dueDate + } + errors + } +} diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index f61bd3e6d68..9882963b580 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -19,6 +19,7 @@ import createBoardListMutation from '../queries/board_list_create.mutation.graph import updateBoardListMutation from '../queries/board_list_update.mutation.graphql'; import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql'; import issueSetLabels from '../queries/issue_set_labels.mutation.graphql'; +import issueSetDueDate from '../queries/issue_set_due_date.mutation.graphql'; const notImplemented = () => { /* eslint-disable-next-line @gitlab/require-i18n-strings */ @@ -327,6 +328,30 @@ export default { }); }, + setActiveIssueDueDate: async ({ commit, getters }, input) => { + const activeIssue = getters.getActiveIssue; + const { data } = await gqlClient.mutate({ + mutation: issueSetDueDate, + variables: { + input: { + iid: String(activeIssue.iid), + projectPath: input.projectPath, + dueDate: input.dueDate, + }, + }, + }); + + if (data.updateIssue?.errors?.length > 0) { + throw new Error(data.updateIssue.errors); + } + + commit(types.UPDATE_ISSUE_BY_ID, { + issueId: activeIssue.id, + prop: 'dueDate', + value: data.updateIssue.issue.dueDate, + }); + }, + fetchBacklog: () => { notImplemented(); }, diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml index e366f5361de..19324b1a002 100644 --- a/app/views/admin/application_settings/_signup.html.haml +++ b/app/views/admin/application_settings/_signup.html.haml @@ -21,6 +21,9 @@ = f.check_box :send_user_confirmation_email, class: 'form-check-input' = f.label :send_user_confirmation_email, class: 'form-check-label' do Send confirmation email on sign-up + + = render_if_exists 'admin/application_settings/new_user_signups_cap', form: f + .form-group = f.label :minimum_password_length, _('Minimum password length (number of characters)'), class: 'label-bold' = f.number_field :minimum_password_length, class: 'form-control', rows: 4, min: ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH, max: Devise.password_length.max diff --git a/app/views/admin/application_settings/network.html.haml b/app/views/admin/application_settings/network.html.haml index 40fa86d8ea3..fabc00a037c 100644 --- a/app/views/admin/application_settings/network.html.haml +++ b/app/views/admin/application_settings/network.html.haml @@ -43,6 +43,10 @@ = expanded_by_default? ? _('Collapse') : _('Expand') %p = _('Configure paths to be protected by Rack Attack.') + .help-block + = _('These paths are protected for POST requests.') + = link_to _('More information'), help_page_path('security/rack_attack', anchor: 'protected-paths-throttle'), target: '_blank' + .settings-content = render 'protected_paths' diff --git a/changelogs/unreleased/235460_remove_sse_erb_support_feature_flag.yml b/changelogs/unreleased/235460_remove_sse_erb_support_feature_flag.yml new file mode 100644 index 00000000000..12a42066a4f --- /dev/null +++ b/changelogs/unreleased/235460_remove_sse_erb_support_feature_flag.yml @@ -0,0 +1,5 @@ +--- +title: Add support for .md.erb files in Static Site Editor +merge_request: 42353 +author: +type: added diff --git a/changelogs/unreleased/vs-admin-new-user-signups.yml b/changelogs/unreleased/vs-admin-new-user-signups.yml new file mode 100644 index 00000000000..10dacdefe7b --- /dev/null +++ b/changelogs/unreleased/vs-admin-new-user-signups.yml @@ -0,0 +1,5 @@ +--- +title: Add migration to add a new configuration option for setting the new user signups count +merge_request: 45643 +author: +type: other diff --git a/config/feature_flags/licensed/resource_access_token.yml b/config/feature_flags/development/resource_access_token_feature.yml similarity index 78% rename from config/feature_flags/licensed/resource_access_token.yml rename to config/feature_flags/development/resource_access_token_feature.yml index c236851881d..3552b4ee69c 100644 --- a/config/feature_flags/licensed/resource_access_token.yml +++ b/config/feature_flags/development/resource_access_token_feature.yml @@ -1,7 +1,7 @@ --- -name: resource_access_token +name: resource_access_token_feature introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29622 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235765 group: group::access -type: licensed +type: development default_enabled: true diff --git a/config/feature_flags/development/sse_erb_support.yml b/config/feature_flags/development/sse_erb_support.yml deleted file mode 100644 index 21a3ad2104b..00000000000 --- a/config/feature_flags/development/sse_erb_support.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: sse_erb_support -introduced_by_url: -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235460 -group: group::static site editor -type: development -default_enabled: false diff --git a/config/initializers/0_inject_feature_flags.rb b/config/initializers/0_inject_feature_flags.rb index 1e0c3c01aba..0b3c97f9f43 100644 --- a/config/initializers/0_inject_feature_flags.rb +++ b/config/initializers/0_inject_feature_flags.rb @@ -14,7 +14,6 @@ if Gitlab.ee? && Gitlab.dev_or_test_env? # being unique to licensed names. These feature flags should be reworked to # be "development" with explicit check IGNORED_FEATURE_FLAGS = %i[ - resource_access_token ci_secrets_management feature_flags_related_issues group_coverage_reports diff --git a/db/migrate/20201019113009_add_new_user_signups_cap_to_application_settings.rb b/db/migrate/20201019113009_add_new_user_signups_cap_to_application_settings.rb new file mode 100644 index 00000000000..8754b0d481e --- /dev/null +++ b/db/migrate/20201019113009_add_new_user_signups_cap_to_application_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddNewUserSignupsCapToApplicationSettings < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + add_column :application_settings, :new_user_signups_cap, :integer + end +end diff --git a/db/schema_migrations/20201019113009 b/db/schema_migrations/20201019113009 new file mode 100644 index 00000000000..88d91b79607 --- /dev/null +++ b/db/schema_migrations/20201019113009 @@ -0,0 +1 @@ +fe57e8e74ebbe0e9567c1487e6e4f8b499afa6404c73424157c43ae79c005f08 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 15bcb7f7632..02d85b58b08 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9305,6 +9305,7 @@ CREATE TABLE application_settings ( secret_detection_token_revocation_url text, encrypted_secret_detection_token_revocation_token text, encrypted_secret_detection_token_revocation_token_iv text, + new_user_signups_cap integer, CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)), CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)), diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index c0a85fc6b70..6d65d402af8 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -347,6 +347,7 @@ proxied proxies proxyable proxying +pseudocode pseudonymized pseudonymizer Puma diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md index a44f97e9a35..67e0c6bb178 100644 --- a/doc/administration/operations/extra_sidekiq_processes.md +++ b/doc/administration/operations/extra_sidekiq_processes.md @@ -112,7 +112,8 @@ you list: ## Queue selector (experimental) -> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/45) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.8. +> - [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/45) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.8. +> - [Sidekiq cluster including queue selector moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab [Core](https://about.gitlab.com/pricing/#self-managed) in GitLab 12.10. CAUTION: **Caution:** As this is marked as **experimental**, it is subject to change at any @@ -130,6 +131,9 @@ in a more general way using the following components: - Attributes that can be selected. - Operators used to construct a query. +When `experimental_queue_selector` is set, all `queue_groups` must be in +the queue selector syntax. + ### Available attributes - [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/261) in GitLab 13.1, `tags`. diff --git a/doc/development/graphql_guide/pagination.md b/doc/development/graphql_guide/pagination.md index bf9eaa99158..287356681ab 100644 --- a/doc/development/graphql_guide/pagination.md +++ b/doc/development/graphql_guide/pagination.md @@ -59,13 +59,13 @@ Some of the benefits and tradeoffs of keyset pagination are - Performance is much better. -- Data stability is greater since you're not going to miss records due to +- More data stability for end-users since records are not missing from lists due to deletions or insertions. - It's the best way to do infinite scrolling. - It's more difficult to program and maintain. Easy for `updated_at` and - `sort_order`, complicated (or impossible) for complex sorting scenarios. + `sort_order`, complicated (or impossible) for [complex sorting scenarios](#limitations-of-query-complexity). ## Implementation @@ -80,7 +80,124 @@ However, there are some cases where we have to use the offset pagination connection, `OffsetActiveRecordRelationConnection`, such as when sorting by label priority in issues, due to the complexity of the sort. - +### Keyset pagination + +The keyset pagination implementation is a subclass of `GraphQL::Pagination::ActiveRecordRelationConnection`, +which is a part of the `graphql` gem. This is installed as the default for all `ActiveRecord::Relation`. +However, instead of using a cursor based on an offset (which is the default), GitLab uses a more specialized cursor. + +The cursor is created by encoding a JSON object which contains the relevant ordering fields. For example: + +```ruby +ordering = {"id"=>"72410125", "created_at"=>"2020-10-08 18:05:21.953398000 UTC"} +json = ordering.to_json +cursor = Base64Bp.urlsafe_encode64(json, padding: false) + +"eyJpZCI6IjcyNDEwMTI1IiwiY3JlYXRlZF9hdCI6IjIwMjAtMTAtMDggMTg6MDU6MjEuOTUzMzk4MDAwIFVUQyJ9" + +json = Base64Bp.urlsafe_decode64(cursor) +Gitlab::Json.parse(json) + +{"id"=>"72410125", "created_at"=>"2020-10-08 18:05:21.953398000 UTC"} +``` + +The benefits of storing the order attribute values in the cursor: + +- If only the ID of the object were stored, the object and its attributes could be queried. + That would require an additional query, and if the object is no longer there, then the needed + attributes are not available. +- If an attribute is `NULL`, then one SQL query can be used. If it's not `NULL`, then a + different SQL query can be used. + +Based on whether the main attribute field being sorted on is `NULL` in the cursor, the proper query +condition is built. The last ordering field is considered to be unique (a primary key), meaning the +column never contains `NULL` values. + +#### Limitations of query complexity + +We only support two ordering fields, and one of those fields needs to be the primary key. + +Here are two examples of pseudocode for the query: + +- **Two-condition query.** `X` represents the values from the cursor. `C` represents + the columns in the database, sorted in ascending order, using an `:after` cursor, and with `NULL` + values sorted last. + + ```plaintext + X1 IS NOT NULL + AND + (C1 > X1) + OR + (C1 IS NULL) + OR + (C1 = X1 + AND + C2 > X2) + + X1 IS NULL + AND + (C1 IS NULL + AND + C2 > X2) + ``` + + Below is an example based on the relation `Issue.order(relative_position: :asc).order(id: :asc)` + with an after cursor of `relative_position: 1500, id: 500`: + + ```plaintext + when cursor[relative_position] is not NULL + + ("issues"."relative_position" > 1500) + OR ( + "issues"."relative_position" = 1500 + AND + "issues"."id" > 500 + ) + OR ("issues"."relative_position" IS NULL) + + when cursor[relative_position] is NULL + + "issues"."relative_position" IS NULL + AND + "issues"."id" > 500 + ``` + +- **Three-condition query.** The example below is not complete, but shows the + complexity of adding one more condition. `X` represents the values from the cursor. `C` represents + the columns in the database, sorted in ascending order, using an `:after` cursor, and with `NULL` + values sorted last. + + ```plaintext + X1 IS NOT NULL + AND + (C1 > X1) + OR + (C1 IS NULL) + OR + (C1 = X1 AND C2 > X2) + OR + (C1 = X1 + AND + X2 IS NOT NULL + AND + ((C2 > X2) + OR + (C2 IS NULL) + OR + (C2 = X2 AND C3 > X3) + OR + X2 IS NULL..... + ``` + +By using +[`Gitlab::Graphql::Pagination::Keyset::QueryBuilder`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/graphql/pagination/keyset/query_builder.rb), +we're able to build the necessary SQL conditions and apply them to the Active Record relation. + +Complex queries can be difficult or impossible to use. For example, +in [`issuable.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/concerns/issuable.rb), +the `order_due_date_and_labels_priority` method creates a very complex query. + +These types of queries are not supported. In these instances, you can use offset pagination. diff --git a/lib/gitlab/static_site_editor/config/generated_config.rb b/lib/gitlab/static_site_editor/config/generated_config.rb index ff24ec69ab0..0a2cee75af7 100644 --- a/lib/gitlab/static_site_editor/config/generated_config.rb +++ b/lib/gitlab/static_site_editor/config/generated_config.rb @@ -34,7 +34,7 @@ module Gitlab delegate :project, to: :repository def supported_extensions - %w[.md].freeze + %w[.md .md.erb].freeze end def commit_id @@ -50,8 +50,6 @@ module Gitlab end def extension_supported? - return true if path.end_with?('.md.erb') && Feature.enabled?(:sse_erb_support, project) - supported_extensions.any? { |ext| path.end_with?(ext) } end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2f9183700a3..845efd57e10 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1914,6 +1914,9 @@ msgstr "" msgid "AdminArea|New user" msgstr "" +msgid "AdminArea|Once the instance reaches the user cap, any user who is added or requests access will have to be approved by an admin. Leave the field empty for unlimited." +msgstr "" + msgid "AdminArea|Owner" msgstr "" @@ -1938,6 +1941,9 @@ msgstr "" msgid "AdminArea|Total users" msgstr "" +msgid "AdminArea|User cap" +msgstr "" + msgid "AdminArea|Users statistics" msgstr "" @@ -2867,6 +2873,9 @@ msgstr "" msgid "An error occurred when toggling the notification subscription" msgstr "" +msgid "An error occurred when updating the issue due date" +msgstr "" + msgid "An error occurred when updating the issue weight" msgstr "" @@ -26978,6 +26987,9 @@ msgstr "" msgid "These existing issues have a similar title. It might be better to comment there instead of creating another similar issue." msgstr "" +msgid "These paths are protected for POST requests." +msgstr "" + msgid "These variables are configured in the parent group settings, and will be active in the current project in addition to the project variables." msgstr "" diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml index 82be2d3a691..bfc35a6abde 100644 --- a/scripts/review_apps/base-config.yaml +++ b/scripts/review_apps/base-config.yaml @@ -52,10 +52,10 @@ gitlab: resources: requests: cpu: 855m - memory: 1071M + memory: 1285M limits: cpu: 1282m - memory: 1606M + memory: 1927M hpa: targetAverageValue: 650m task-runner: diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js new file mode 100644 index 00000000000..b034c8cb11d --- /dev/null +++ b/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js @@ -0,0 +1,137 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlDatepicker } from '@gitlab/ui'; +import BoardSidebarDueDate from '~/boards/components/sidebar/board_sidebar_due_date.vue'; +import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue'; +import { createStore } from '~/boards/stores'; +import createFlash from '~/flash'; + +const TEST_DUE_DATE = '2020-02-20'; +const TEST_FORMATTED_DUE_DATE = 'Feb 20, 2020'; +const TEST_PARSED_DATE = new Date(2020, 1, 20); +const TEST_ISSUE = { id: 'gid://gitlab/Issue/1', iid: 9, dueDate: null, referencePath: 'h/b#2' }; + +jest.mock('~/flash'); + +describe('~/boards/components/sidebar/board_sidebar_due_date.vue', () => { + let wrapper; + let store; + + afterEach(() => { + wrapper.destroy(); + store = null; + wrapper = null; + }); + + const createWrapper = ({ dueDate = null } = {}) => { + store = createStore(); + store.state.issues = { [TEST_ISSUE.id]: { ...TEST_ISSUE, dueDate } }; + store.state.activeId = TEST_ISSUE.id; + + wrapper = shallowMount(BoardSidebarDueDate, { + store, + provide: { + canUpdate: true, + }, + stubs: { + 'board-editable-item': BoardEditableItem, + }, + }); + }; + + const findDatePicker = () => wrapper.find(GlDatepicker); + const findResetButton = () => wrapper.find('[data-testid="reset-button"]'); + const findCollapsed = () => wrapper.find('[data-testid="collapsed-content"]'); + + it('renders "None" when no due date is set', () => { + createWrapper(); + + expect(findCollapsed().text()).toBe('None'); + expect(findResetButton().exists()).toBe(false); + }); + + it('renders formatted due date with reset button when set', () => { + createWrapper({ dueDate: TEST_DUE_DATE }); + + expect(findCollapsed().text()).toContain(TEST_FORMATTED_DUE_DATE); + expect(findResetButton().exists()).toBe(true); + }); + + describe('when due date is submitted', () => { + beforeEach(async () => { + createWrapper(); + + jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { + store.state.issues[TEST_ISSUE.id].dueDate = TEST_DUE_DATE; + }); + findDatePicker().vm.$emit('input', TEST_PARSED_DATE); + await wrapper.vm.$nextTick(); + }); + + it('collapses sidebar and renders formatted due date with reset button', () => { + expect(findCollapsed().isVisible()).toBe(true); + expect(findCollapsed().text()).toContain(TEST_FORMATTED_DUE_DATE); + expect(findResetButton().exists()).toBe(true); + }); + + it('commits change to the server', () => { + expect(wrapper.vm.setActiveIssueDueDate).toHaveBeenCalledWith({ + dueDate: TEST_DUE_DATE, + projectPath: 'h/b', + }); + }); + }); + + describe('when due date is cleared', () => { + beforeEach(async () => { + createWrapper(); + + jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { + store.state.issues[TEST_ISSUE.id].dueDate = null; + }); + findDatePicker().vm.$emit('clear'); + await wrapper.vm.$nextTick(); + }); + + it('collapses sidebar and renders "None"', () => { + expect(wrapper.vm.setActiveIssueDueDate).toHaveBeenCalled(); + expect(findCollapsed().isVisible()).toBe(true); + expect(findCollapsed().text()).toBe('None'); + }); + }); + + describe('when due date is resetted', () => { + beforeEach(async () => { + createWrapper({ dueDate: TEST_DUE_DATE }); + + jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { + store.state.issues[TEST_ISSUE.id].dueDate = null; + }); + findResetButton().vm.$emit('click'); + await wrapper.vm.$nextTick(); + }); + + it('collapses sidebar and renders "None"', () => { + expect(wrapper.vm.setActiveIssueDueDate).toHaveBeenCalled(); + expect(findCollapsed().isVisible()).toBe(true); + expect(findCollapsed().text()).toBe('None'); + }); + }); + + describe('when the mutation fails', () => { + beforeEach(async () => { + createWrapper({ dueDate: TEST_DUE_DATE }); + + jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { + throw new Error(['failed mutation']); + }); + findDatePicker().vm.$emit('input', 'Invalid date'); + await wrapper.vm.$nextTick(); + }); + + it('collapses sidebar and renders former issue due date', () => { + expect(findCollapsed().isVisible()).toBe(true); + expect(findCollapsed().text()).toContain(TEST_FORMATTED_DUE_DATE); + expect(createFlash).toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index 97b2039b24b..e6315d63364 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -640,6 +640,57 @@ describe('setActiveIssueLabels', () => { }); }); +describe('setActiveIssueDueDate', () => { + const state = { issues: { [mockIssue.id]: mockIssue } }; + const getters = { getActiveIssue: mockIssue }; + const testDueDate = '2020-02-20'; + const input = { + dueDate: testDueDate, + projectPath: 'h/b', + }; + + it('should commit due date after setting the issue', done => { + jest.spyOn(gqlClient, 'mutate').mockResolvedValue({ + data: { + updateIssue: { + issue: { + dueDate: testDueDate, + }, + errors: [], + }, + }, + }); + + const payload = { + issueId: getters.getActiveIssue.id, + prop: 'dueDate', + value: testDueDate, + }; + + testAction( + actions.setActiveIssueDueDate, + input, + { ...state, ...getters }, + [ + { + type: types.UPDATE_ISSUE_BY_ID, + payload, + }, + ], + [], + done, + ); + }); + + it('throws error if fails', async () => { + jest + .spyOn(gqlClient, 'mutate') + .mockResolvedValue({ data: { updateIssue: { errors: ['failed mutation'] } } }); + + await expect(actions.setActiveIssueDueDate({ getters }, input)).rejects.toThrow(Error); + }); +}); + describe('fetchBacklog', () => { expectNotImplemented(actions.fetchBacklog); }); diff --git a/spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb b/spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb index 2f761b69e60..0b2055d3db5 100644 --- a/spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb +++ b/spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb @@ -58,25 +58,9 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do ) end - context 'when feature flag is enabled' do - let(:path) { 'FEATURE_ON.md.erb' } + let(:path) { 'README.md.erb' } - before do - stub_feature_flags(sse_erb_support: project) - end - - it { is_expected.to include(is_supported_content: true) } - end - - context 'when feature flag is disabled' do - let(:path) { 'FEATURE_OFF.md.erb' } - - before do - stub_feature_flags(sse_erb_support: false) - end - - it { is_expected.to include(is_supported_content: false) } - end + it { is_expected.to include(is_supported_content: true) } end context 'when file path is nested' do