提交 c2908ec6 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 45999bfd
......@@ -3,7 +3,7 @@
import AccessorUtilities from './lib/utils/accessor';
export default class Autosave {
constructor(field, key, fallbackKey) {
constructor(field, key, fallbackKey, lockVersion) {
this.field = field;
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
......@@ -12,6 +12,8 @@ export default class Autosave {
}
this.key = `autosave/${key}`;
this.fallbackKey = fallbackKey;
this.lockVersionKey = `${this.key}/lockVersion`;
this.lockVersion = lockVersion;
this.field.data('autosave', this);
this.restore();
this.field.on('input', () => this.save());
......@@ -40,6 +42,11 @@ export default class Autosave {
}
}
getSavedLockVersion() {
if (!this.isLocalStorageAvailable) return;
return window.localStorage.getItem(this.lockVersionKey);
}
save() {
if (!this.field.length) return;
......@@ -49,6 +56,9 @@ export default class Autosave {
if (this.fallbackKey) {
window.localStorage.setItem(this.fallbackKey, text);
}
if (this.lockVersion !== undefined) {
window.localStorage.setItem(this.lockVersionKey, this.lockVersion);
}
return window.localStorage.setItem(this.key, text);
}
......@@ -58,6 +68,7 @@ export default class Autosave {
reset() {
if (!this.isLocalStorageAvailable) return;
window.localStorage.removeItem(this.lockVersionKey);
window.localStorage.removeItem(this.fallbackKey);
return window.localStorage.removeItem(this.key);
}
......
......@@ -75,6 +75,7 @@ export default {
<template #actions>
<clone-dropdown-button
v-if="canBeCloned"
class="mr-2"
:ssh-link="snippet.sshUrlToRepo"
:http-link="snippet.httpUrlToRepo"
/>
......
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import StaticSiteEditor from './components/static_site_editor.vue';
import createStore from './store';
const initStaticSiteEditor = el => {
const { projectId, path: sourcePath, returnUrl } = el.dataset;
const isSupportedContent = 'isSupportedContent' in el.dataset;
const { isSupportedContent, projectId, path: sourcePath, returnUrl } = el.dataset;
const store = createStore({
initialState: {
isSupportedContent,
isSupportedContent: parseBoolean(isSupportedContent),
projectId,
returnUrl,
sourcePath,
......
......@@ -12,7 +12,7 @@ class Projects::RefsController < Projects::ApplicationController
before_action :authorize_download_code!
before_action only: [:logs_tree] do
push_frontend_feature_flag(:vue_file_list_lfs_badge)
push_frontend_feature_flag(:vue_file_list_lfs_badge, default_enabled: true)
end
def switch
......
......@@ -16,7 +16,7 @@ class Projects::TreeController < Projects::ApplicationController
before_action :authorize_edit_tree!, only: [:create_dir]
before_action only: [:show] do
push_frontend_feature_flag(:vue_file_list_lfs_badge)
push_frontend_feature_flag(:vue_file_list_lfs_badge, default_enabled: true)
end
def show
......
......@@ -25,4 +25,8 @@ class ResourceMilestoneEvent < ResourceEvent
def self.issuable_attrs
%i(issue merge_request).freeze
end
def milestone_title
milestone&.title
end
end
......@@ -67,22 +67,30 @@ module Issuable
end
def copy_resource_milestone_events
entity_key = new_entity.class.name.underscore.foreign_key
return unless milestone_events_supported?
copy_events(ResourceMilestoneEvent.table_name, original_entity.resource_milestone_events) do |event|
matching_destination_milestone = matching_milestone(event.milestone.title)
if matching_destination_milestone.present?
event.attributes
.except('id')
.merge(entity_key => new_entity.id,
'milestone_id' => matching_destination_milestone.id,
'action' => ResourceMilestoneEvent.actions[event.action],
'state' => ResourceMilestoneEvent.states[event.state])
if event.remove?
event_attributes_with_milestone(event, nil)
else
matching_destination_milestone = matching_milestone(event.milestone_title)
event_attributes_with_milestone(event, matching_destination_milestone) if matching_destination_milestone.present?
end
end
end
def event_attributes_with_milestone(event, milestone)
entity_key = new_entity.class.name.underscore.foreign_key
event.attributes
.except('id')
.merge(entity_key => new_entity.id,
'milestone_id' => milestone&.id,
'action' => ResourceMilestoneEvent.actions[event.action],
'state' => ResourceMilestoneEvent.states[event.state])
end
def copy_events(table_name, events_to_copy)
events_to_copy.find_in_batches do |batch|
events = batch.map do |event|
......@@ -96,6 +104,11 @@ module Issuable
def entity_key
new_entity.class.name.parameterize('_').foreign_key
end
def milestone_events_supported?
original_entity.respond_to?(:resource_milestone_events) &&
new_entity.respond_to?(:resource_milestone_events)
end
end
end
end
---
title: Create operations_strategies_user_lists table
merge_request: 30243
author:
type: added
---
title: Added right margin to Clone Snippet button
merge_request: 30471
author:
type: fixed
# frozen_string_literal: true
class CreateOperationsStrategiesUserLists < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
create_table :operations_strategies_user_lists do |t|
t.references :strategy, index: false, foreign_key: { on_delete: :cascade, to_table: :operations_strategies }, null: false
t.references :user_list, index: true, foreign_key: { on_delete: :cascade, to_table: :operations_user_lists }, null: false
t.index [:strategy_id, :user_list_id], unique: true, name: :index_ops_strategies_user_lists_on_strategy_id_and_user_list_id
end
end
end
......@@ -4415,6 +4415,21 @@ CREATE SEQUENCE public.operations_strategies_id_seq
ALTER SEQUENCE public.operations_strategies_id_seq OWNED BY public.operations_strategies.id;
CREATE TABLE public.operations_strategies_user_lists (
id bigint NOT NULL,
strategy_id bigint NOT NULL,
user_list_id bigint NOT NULL
);
CREATE SEQUENCE public.operations_strategies_user_lists_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.operations_strategies_user_lists_id_seq OWNED BY public.operations_strategies_user_lists.id;
CREATE TABLE public.operations_user_lists (
id bigint NOT NULL,
project_id bigint NOT NULL,
......@@ -7489,6 +7504,8 @@ ALTER TABLE ONLY public.operations_scopes ALTER COLUMN id SET DEFAULT nextval('p
ALTER TABLE ONLY public.operations_strategies ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_id_seq'::regclass);
ALTER TABLE ONLY public.operations_strategies_user_lists ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_user_lists_id_seq'::regclass);
ALTER TABLE ONLY public.operations_user_lists ALTER COLUMN id SET DEFAULT nextval('public.operations_user_lists_id_seq'::regclass);
ALTER TABLE ONLY public.packages_build_infos ALTER COLUMN id SET DEFAULT nextval('public.packages_build_infos_id_seq'::regclass);
......@@ -8314,6 +8331,9 @@ ALTER TABLE ONLY public.operations_scopes
ALTER TABLE ONLY public.operations_strategies
ADD CONSTRAINT operations_strategies_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.operations_strategies_user_lists
ADD CONSTRAINT operations_strategies_user_lists_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.operations_user_lists
ADD CONSTRAINT operations_user_lists_pkey PRIMARY KEY (id);
......@@ -9923,10 +9943,14 @@ CREATE UNIQUE INDEX index_operations_scopes_on_strategy_id_and_environment_scope
CREATE INDEX index_operations_strategies_on_feature_flag_id ON public.operations_strategies USING btree (feature_flag_id);
CREATE INDEX index_operations_strategies_user_lists_on_user_list_id ON public.operations_strategies_user_lists USING btree (user_list_id);
CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_iid ON public.operations_user_lists USING btree (project_id, iid);
CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_name ON public.operations_user_lists USING btree (project_id, name);
CREATE UNIQUE INDEX index_ops_strategies_user_lists_on_strategy_id_and_user_list_id ON public.operations_strategies_user_lists USING btree (strategy_id, user_list_id);
CREATE UNIQUE INDEX index_packages_build_infos_on_package_id ON public.packages_build_infos USING btree (package_id);
CREATE INDEX index_packages_build_infos_on_pipeline_id ON public.packages_build_infos USING btree (pipeline_id);
......@@ -11584,6 +11608,9 @@ ALTER TABLE ONLY public.ci_resources
ALTER TABLE ONLY public.clusters_applications_fluentd
ADD CONSTRAINT fk_rails_4319b1dcd2 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.operations_strategies_user_lists
ADD CONSTRAINT fk_rails_43241e8d29 FOREIGN KEY (strategy_id) REFERENCES public.operations_strategies(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.lfs_file_locks
ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
......@@ -12157,6 +12184,9 @@ ALTER TABLE ONLY public.ci_daily_report_results
ALTER TABLE ONLY public.issues_self_managed_prometheus_alert_events
ADD CONSTRAINT fk_rails_cc5d88bbb0 FOREIGN KEY (issue_id) REFERENCES public.issues(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.operations_strategies_user_lists
ADD CONSTRAINT fk_rails_ccb7e4bc0b FOREIGN KEY (user_list_id) REFERENCES public.operations_user_lists(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.issue_tracker_data
ADD CONSTRAINT fk_rails_ccc0840427 FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE CASCADE;
......@@ -13486,6 +13516,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200420172927
20200420201933
20200421233150
20200422213749
20200423075720
20200423080334
20200423080607
......
......@@ -11,6 +11,7 @@ Updating Geo nodes involves performing:
Depending on which version of Geo you are updating to/from, there may be
different steps.
- [Updating to GitLab 12.9](version_specific_updates.md#updating-to-gitlab-129)
- [Updating to GitLab 12.7](version_specific_updates.md#updating-to-gitlab-127)
- [Updating to GitLab 12.2](version_specific_updates.md#updating-to-gitlab-122)
- [Updating to GitLab 12.1](version_specific_updates.md#updating-to-gitlab-121)
......
......@@ -76,16 +76,16 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| Setting | Description | Default |
|---------|-------------|---------|
| `provider` | Always `AWS` for compatible hosts | AWS |
| `provider` | Always `AWS` for compatible hosts | `AWS` |
| `aws_access_key_id` | AWS credentials, or compatible | |
| `aws_secret_access_key` | AWS credentials, or compatible | |
| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true |
| `aws_signature_version` | AWS signature version to use. `2` or `4` are valid options. Digital Ocean Spaces and other providers may need `2`. | `4` |
| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be `false`. | `true` |
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | `s3.amazonaws.com` |
| `endpoint` | Can be used when configuring an S3 compatible service such as [MinIO](https://min.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
| `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false |
| `use_iam_profile` | Set to true to use IAM profile instead of access keys | false
| `path_style` | Set to `true` to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as `false` for AWS S3. | `false` |
| `use_iam_profile` | Set to `true` to use IAM profile instead of access keys | false
**In Omnibus installations:**
......@@ -149,8 +149,8 @@ Note that Oracle Cloud S3 must be sure to use the following settings:
| Setting | Value |
|---------|-------|
| `enable_signature_v4_streaming` | false |
| `path_style` | true |
| `enable_signature_v4_streaming` | `false` |
| `path_style` | `true` |
If `enable_signature_v4_streaming` is set to `true`, you may see the
following error:
......@@ -165,7 +165,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| Setting | Description | Default |
|---------|-------------|---------|
| `provider` | Always `OpenStack` for compatible hosts | OpenStack |
| `provider` | Always `OpenStack` for compatible hosts | `OpenStack` |
| `openstack_username` | OpenStack username | |
| `openstack_api_key` | OpenStack API key | |
| `openstack_temp_url_key` | OpenStack key for generating temporary urls | |
......
......@@ -630,7 +630,7 @@ the `epic` property:
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
**Note**: The `epic_iid` attribute is deprecated and [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157).
**Note**: The `epic_iid` attribute is deprecated and [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/issues/35157).
Please use `iid` of the `epic` attribute instead.
## New issue
......@@ -657,7 +657,7 @@ POST /projects/:id/issues
| `discussion_to_resolve` | string | no | The ID of a discussion to resolve. This will fill in the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. |
| `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. |
| `epic_id` **(ULTIMATE)** | integer | no | ID of the epic to add the issue to. Valid values are greater than or equal to 0. |
| `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157)) |
| `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/issues/35157)) |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues?title=Issues%20with%20auth&labels=bug
......@@ -773,7 +773,7 @@ PUT /projects/:id/issues/:issue_iid
| `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. 0 |
| `discussion_locked` | boolean | no | Flag indicating if the issue's discussion is locked. If the discussion is locked only project members can add or edit comments. |
| `epic_id` **(ULTIMATE)** | integer | no | ID of the epic to add the issue to. Valid values are greater than or equal to 0. |
| `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157)) |
| `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/issues/35157)) |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues/85?state_event=close
......
......@@ -142,21 +142,31 @@ Starting with GitLab 12.0, Git is required to be compiled with `libpcre2`.
Find out if that's the case:
```shell
ldd /usr/local/bin/git | grep pcre2
ldd $(which git) | grep pcre2
```
The output should be similar to:
The output should contain `libpcre2-8.so.0`.
```plaintext
libpcre2-8.so.0 => /usr/lib/libpcre2-8.so.0 (0x00007f08461c3000)
Is the system packaged Git too old, or not compiled with pcre2?
Remove it:
```shell
sudo apt-get remove git-core
```
Is the system packaged Git too old, or not compiled with pcre2? Remove it and compile from source:
On Ubuntu, install Git from [its official PPA](https://git-scm.com/download/linux):
```shell
# Remove packaged Git
sudo apt-get remove git-core
# run as root!
add-apt-repository ppa:git-core/ppa
apt update
apt install git
# repeat libpcre2 check as above
```
On Debian, use the following compilation instructions:
```shell
# Install dependencies
sudo apt-get install -y libcurl4-openssl-dev libexpat1-dev gettext libz-dev libssl-dev build-essential
......@@ -180,7 +190,7 @@ make prefix=/usr/local all
# Install into /usr/local/bin
sudo make prefix=/usr/local install
# When editing config/gitlab.yml (Step 5), change the git -> bin_path to /usr/local/bin/git
# When editing config/gitlab.yml later, change the git -> bin_path to /usr/local/bin/git
```
For the [Custom Favicon](../user/admin_area/appearance.md#favicon) to work, GraphicsMagick
......
......@@ -51,7 +51,7 @@ that are in common for all providers that we need to consider.
automatically create an account. It defaults to `false`. If `false` users must
be created manually or they will not be able to sign in via OmniAuth.
- `auto_link_ldap_user` can be used if you have [LDAP / ActiveDirectory](ldap.md)
integration enabled. It defaults to false. When enabled, users automatically
integration enabled. It defaults to `false`. When enabled, users automatically
created through an OmniAuth provider will have their LDAP identity created in GitLab as well.
- `block_auto_created_users` defaults to `true`. If `true` auto created users will
be blocked by default and will have to be unblocked by an administrator before
......
......@@ -112,7 +112,6 @@ are adding support for [PHP](https://gitlab.com/gitlab-org/gitlab/-/merge_reques
| [Opkg](https://gitlab.com/gitlab-org/gitlab/issues/36894) | Optimize your work with OpenWrt using Opkg repositories. |
| [P2](https://gitlab.com/gitlab-org/gitlab/issues/36895) | Host all your Eclipse plugins in your own GitLab P2 repository. |
| [Puppet](https://gitlab.com/gitlab-org/gitlab/issues/36897) | Configuration management meets repository management with Puppet repositories. |
| [PyPi](https://gitlab.com/gitlab-org/gitlab/issues/10483) | Host PyPi distributions. |
| [RPM](https://gitlab.com/gitlab-org/gitlab/issues/5932) | Distribute RPMs directly from GitLab. |
| [RubyGems](https://gitlab.com/gitlab-org/gitlab/issues/803) | Use GitLab to host your own gems. |
| [SBT](https://gitlab.com/gitlab-org/gitlab/issues/36898) | Resolve dependencies from and deploy build output to SBT repositories when running SBT builds. |
......
......@@ -363,6 +363,14 @@ You do not need a token to run `npm install` unless your project is private (the
NPM_TOKEN=<your_token> npm install
```
### `npm install` returns `npm ERR! 403 Forbidden`
- Check that your token is not expired and has appropriate permissions.
- Check if you have attempted to publish a package with a name that already exists within a given scope.
- Ensure the scoped packages URL includes a trailing slash:
- Correct: `//gitlab.com/api/v4/packages/npm/`
- Incorrect: `//gitlab.com/api/v4/packages/npm`
## NPM dependencies metadata
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11867) in GitLab Premium 12.6.
......
......@@ -4,7 +4,8 @@
Working with multiple people on the same file can be a risk. Conflicts when merging a non-text file are hard to overcome and will require a lot of manual work to resolve. File Locking helps you avoid these merge conflicts and better manage your binary files.
With File Locaking, you can lock any file or directory, make your changes, and then unlock it so another member of the team can edit it.
With File Locking, you can lock any file or directory, make your changes, and
then unlock it so another member of the team can edit it.
## Overview
......
......@@ -24,6 +24,11 @@ Changes are saved immediately.
![Edit a due date via the sidebar](img/due_dates_edit_sidebar.png)
The last way to set a due date is by using [quick actions](../quick_actions.md), directly in an issue's description or comment:
- `/due <date>`: set due date. Examples of valid `<date>` include `in 2 days`, `this Friday`, and `December 31st`.
- `/remove_due_date`: remove due date.
## Making use of due dates
Issues that have a due date can be easily seen in the issue tracker,
......
......@@ -22,7 +22,7 @@ module Gitlab
project: project.path,
namespace: project.namespace.path,
return_url: return_url,
is_supported_content: supported_content?
is_supported_content: supported_content?.to_s
}
end
......
......@@ -2994,9 +2994,6 @@ msgstr ""
msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo."
msgstr ""
msgid "Batch operations"
msgstr ""
msgid "BatchComments|Delete all pending comments"
msgstr ""
......@@ -9215,7 +9212,10 @@ msgstr ""
msgid "Filter by milestone name"
msgstr ""
msgid "Filter by name..."
msgid "Filter by name"
msgstr ""
msgid "Filter by status"
msgstr ""
msgid "Filter by two-factor authentication"
......@@ -11208,6 +11208,9 @@ msgstr ""
msgid "In order to tailor your experience with GitLab we<br>would like to know a bit more about you."
msgstr ""
msgid "In progress"
msgstr ""
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
......@@ -17592,6 +17595,9 @@ msgstr ""
msgid "Resync"
msgstr ""
msgid "Resync all"
msgstr ""
msgid "Resync all %{replicableType}"
msgstr ""
......
......@@ -10,6 +10,8 @@ describe('Autosave', () => {
const field = $('<textarea></textarea>');
const key = 'key';
const fallbackKey = 'fallbackKey';
const lockVersionKey = 'lockVersionKey';
const lockVersion = 1;
describe('class constructor', () => {
beforeEach(() => {
......@@ -30,6 +32,13 @@ describe('Autosave', () => {
expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
expect(autosave.isLocalStorageAvailable).toBe(true);
});
it('should set .isLocalStorageAvailable if lockVersion is passed', () => {
autosave = new Autosave(field, key, null, lockVersion);
expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
expect(autosave.isLocalStorageAvailable).toBe(true);
});
});
describe('restore', () => {
......@@ -96,6 +105,40 @@ describe('Autosave', () => {
});
});
describe('getSavedLockVersion', () => {
beforeEach(() => {
autosave = {
field,
key,
lockVersionKey,
};
});
describe('if .isLocalStorageAvailable is `false`', () => {
beforeEach(() => {
autosave.isLocalStorageAvailable = false;
Autosave.prototype.getSavedLockVersion.call(autosave);
});
it('should not call .getItem', () => {
expect(window.localStorage.getItem).not.toHaveBeenCalled();
});
});
describe('if .isLocalStorageAvailable is `true`', () => {
beforeEach(() => {
autosave.isLocalStorageAvailable = true;
});
it('should call .getItem', () => {
Autosave.prototype.getSavedLockVersion.call(autosave);
expect(window.localStorage.getItem).toHaveBeenCalledWith(lockVersionKey);
});
});
});
describe('save', () => {
beforeEach(() => {
autosave = { reset: jest.fn() };
......@@ -128,10 +171,51 @@ describe('Autosave', () => {
});
});
describe('save with lockVersion', () => {
beforeEach(() => {
autosave = {
field,
key,
lockVersionKey,
lockVersion,
isLocalStorageAvailable: true,
};
});
describe('lockVersion is valid', () => {
it('should call .setItem', () => {
Autosave.prototype.save.call(autosave);
expect(window.localStorage.setItem).toHaveBeenCalledWith(lockVersionKey, lockVersion);
});
it('should call .setItem when version is 0', () => {
autosave.lockVersion = 0;
Autosave.prototype.save.call(autosave);
expect(window.localStorage.setItem).toHaveBeenCalledWith(
lockVersionKey,
autosave.lockVersion,
);
});
});
describe('lockVersion is invalid', () => {
it('should not call .setItem with lockVersion', () => {
delete autosave.lockVersion;
Autosave.prototype.save.call(autosave);
expect(window.localStorage.setItem).not.toHaveBeenCalledWith(
lockVersionKey,
autosave.lockVersion,
);
});
});
});
describe('reset', () => {
beforeEach(() => {
autosave = {
key,
lockVersionKey,
};
});
......@@ -156,6 +240,7 @@ describe('Autosave', () => {
it('should call .removeItem', () => {
expect(window.localStorage.removeItem).toHaveBeenCalledWith(key);
expect(window.localStorage.removeItem).toHaveBeenCalledWith(lockVersionKey);
});
});
});
......@@ -166,8 +251,8 @@ describe('Autosave', () => {
field,
key,
fallbackKey,
isLocalStorageAvailable: true,
};
autosave.isLocalStorageAvailable = true;
});
it('should call .getItem', () => {
......@@ -185,7 +270,8 @@ describe('Autosave', () => {
it('should call .removeItem for key and fallbackKey', () => {
Autosave.prototype.reset.call(autosave);
expect(window.localStorage.removeItem).toHaveBeenCalledTimes(2);
expect(window.localStorage.removeItem).toHaveBeenCalledWith(fallbackKey);
expect(window.localStorage.removeItem).toHaveBeenCalledWith(key);
});
});
});
......@@ -24,38 +24,38 @@ describe Gitlab::StaticSiteEditor::Config do
project: 'project',
project_id: project.id,
return_url: 'http://example.com',
is_supported_content: true
is_supported_content: 'true'
)
end
context 'when branch is not master' do
let(:ref) { 'my-branch' }
it { is_expected.to include(is_supported_content: false) }
it { is_expected.to include(is_supported_content: 'false') }
end
context 'when file does not have a markdown extension' do
let(:file_path) { 'README.txt' }
it { is_expected.to include(is_supported_content: false) }
it { is_expected.to include(is_supported_content: 'false') }
end
context 'when file does not have an extension' do
let(:file_path) { 'README' }
it { is_expected.to include(is_supported_content: false) }
it { is_expected.to include(is_supported_content: 'false') }
end
context 'when file does not exist' do
let(:file_path) { 'UNKNOWN.md' }
it { is_expected.to include(is_supported_content: false) }
it { is_expected.to include(is_supported_content: 'false') }
end
context 'when repository is empty' do
let(:project) { create(:project_empty_repo) }
it { is_expected.to include(is_supported_content: false) }
it { is_expected.to include(is_supported_content: 'false') }
end
end
end
......@@ -78,4 +78,21 @@ describe ResourceMilestoneEvent, type: :model do
let(:query_method) { :remove? }
end
end
describe '#milestone_title' do
let(:milestone) { create(:milestone, title: 'v2.3') }
let(:event) { create(:resource_milestone_event, milestone: milestone) }
it 'returns the expected title' do
expect(event.milestone_title).to eq('v2.3')
end
context 'when milestone is nil' do
let(:event) { create(:resource_milestone_event, milestone: nil) }
it 'returns nil' do
expect(event.milestone_title).to be_nil
end
end
end
end
......@@ -78,6 +78,14 @@ describe API::Terraform::State do
expect(response).to have_gitlab_http_status(:ok)
end
context 'on Unicorn', :unicorn do
it 'updates the state' do
expect { request }.to change { Terraform::State.count }.by(0)
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'without body' do
......@@ -112,6 +120,14 @@ describe API::Terraform::State do
expect(response).to have_gitlab_http_status(:ok)
end
context 'on Unicorn', :unicorn do
it 'creates a new state' do
expect { request }.to change { Terraform::State.count }.by(1)
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'without body' do
......
......@@ -89,7 +89,7 @@ describe Issuable::Clone::AttributesRewriter do
create_event(milestone1_project1)
create_event(milestone2_project1)
create_event(milestone1_project1, 'remove')
create_event(nil, 'remove')
create_event(milestone3_project1)
end
......@@ -101,7 +101,7 @@ describe Issuable::Clone::AttributesRewriter do
expect_milestone_event(new_issue_milestone_events.first, milestone: milestone1_project2, action: 'add', state: 'opened')
expect_milestone_event(new_issue_milestone_events.second, milestone: milestone2_project2, action: 'add', state: 'opened')
expect_milestone_event(new_issue_milestone_events.third, milestone: milestone1_project2, action: 'remove', state: 'opened')
expect_milestone_event(new_issue_milestone_events.third, milestone: nil, action: 'remove', state: 'opened')
end
def create_event(milestone, action = 'add')
......@@ -109,7 +109,7 @@ describe Issuable::Clone::AttributesRewriter do
end
def expect_milestone_event(event, expected_attrs)
expect(event.milestone_id).to eq(expected_attrs[:milestone].id)
expect(event.milestone_id).to eq(expected_attrs[:milestone]&.id)
expect(event.action).to eq(expected_attrs[:action])
expect(event.state).to eq(expected_attrs[:state])
end
......
# frozen_string_literal: true
REQUEST_CLASSES = [
::Grape::Request,
::Rack::Request
].freeze
def request_body_class
return ::Unicorn::TeeInput if defined?(::Unicorn)
Class.new(StringIO) do
def string
raise NotImplementedError, '#string is only valid under Puma which uses StringIO, use #read instead'
end
end
end
RSpec.configure do |config|
config.before(:each, :unicorn) do
REQUEST_CLASSES.each do |request_class|
allow_any_instance_of(request_class)
.to receive(:body).and_wrap_original do |m, *args|
request_body_class.new(m.call(*args).read)
end
end
end
end
......@@ -786,10 +786,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.121.0.tgz#77083a68f72e9aa0e294da7715f378eef13b839e"
integrity sha512-scz/6Y/eED7RMFLAlhT6PwXwe0Wj8ivnRsyulk9NXKoqUmAqZliNmBmzYsHy5bFf9NB6xVV/rOk1/92nbi/Yaw==
"@gitlab/ui@12.3.0":
version "12.3.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-12.3.0.tgz#9234205887675a6d13a51945ee62efc3c8b5e890"
integrity sha512-XrHC2pK7qlwy6K3OR/+iCP8TDewn3jaDIHCfHjt/KOwvD5LsEmam9RHjTiZ4epPZXLv4+JxCzbc4R+euEbIQ7g==
"@gitlab/ui@13.5.0":
version "13.5.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-13.5.0.tgz#bb8d90baea80066e5360457386d8153724998043"
integrity sha512-f4k6zKcJWRNV5ho7SXz0gL4VU4n+ljB52VrUrfJ1WTrESGpIFlTU17/Ac4ZMYySZuUXzmLulf9BXEN5HWCetTQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册