提交 4be784ea 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 57ac0bc8
......@@ -42,7 +42,7 @@ docs lint:
extends:
- .default-retry
- .docs:rules:docs-lint
image: "registry.gitlab.com/gitlab-org/gitlab-docs:lint"
image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:vale-2.3.3-markdownlint-0.23.2"
stage: test
needs: []
script:
......
......@@ -26,7 +26,7 @@ gem 'marginalia', '~> 1.9.0'
# Authentication libraries
gem 'devise', '~> 4.6'
gem 'doorkeeper', '~> 5.0.3'
gem 'doorkeeper', '~> 5.1.1'
gem 'doorkeeper-openid_connect', '~> 1.6.3'
gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 2.0.0'
......
......@@ -254,8 +254,8 @@ GEM
docile (1.3.2)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.0.3)
railties (>= 4.2)
doorkeeper (5.1.1)
railties (>= 5)
doorkeeper-openid_connect (1.6.3)
doorkeeper (>= 5.0, < 5.2)
json-jwt (~> 1.6)
......@@ -1265,7 +1265,7 @@ DEPENDENCIES
diff_match_patch (~> 0.1.0)
diffy (~> 3.3)
discordrb-webhooks-blackst0ne (~> 3.3)
doorkeeper (~> 5.0.3)
doorkeeper (~> 5.1.1)
doorkeeper-openid_connect (~> 1.6.3)
ed25519 (~> 1.2)
elasticsearch-api (~> 6.8)
......
......@@ -62,22 +62,20 @@ export default {
activeDiscussion: {
query: activeDiscussionQuery,
result({ data }) {
const discussionId = data.activeDiscussion.id;
if (this.discussion.resolved && !this.resolvedDiscussionsExpanded) {
return;
}
// We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists
// We don't want scrollIntoView to be triggered from the discussion click itself
if (
discussionId &&
data.activeDiscussion.source === ACTIVE_DISCUSSION_SOURCE_TYPES.pin &&
discussionId === this.discussion.notes[0].id
) {
this.$el.scrollIntoView({
behavior: 'smooth',
inline: 'start',
});
}
this.$nextTick(() => {
// We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists.
// We don't want scrollIntoView to be triggered from the discussion click itself.
if (this.$el && this.shouldScrollToDiscussion(data.activeDiscussion)) {
this.$el.scrollIntoView({
behavior: 'smooth',
inline: 'start',
});
}
});
},
},
},
......@@ -136,6 +134,18 @@ export default {
isFormVisible() {
return this.isFormRendered && this.discussionWithOpenForm === this.discussion.id;
},
shouldScrollToDiscussion(activeDiscussion) {
const ALLOWED_ACTIVE_DISCUSSION_SOURCES = [
ACTIVE_DISCUSSION_SOURCE_TYPES.pin,
ACTIVE_DISCUSSION_SOURCE_TYPES.url,
];
const { id: activeDiscussionId, source: activeDiscussionSource } = activeDiscussion;
return (
ALLOWED_ACTIVE_DISCUSSION_SOURCES.includes(activeDiscussionSource) &&
activeDiscussionId === this.discussion.notes[0].id
);
},
},
methods: {
addDiscussionComment(
......
......@@ -60,9 +60,11 @@ export default {
},
},
mounted() {
if (this.isNoteLinked) {
this.$el.scrollIntoView({ behavior: 'smooth', inline: 'start' });
}
this.$nextTick(() => {
if (this.isNoteLinked) {
this.$el.scrollIntoView({ behavior: 'smooth', inline: 'start' });
}
});
},
methods: {
hideForm() {
......
......@@ -11,6 +11,7 @@ export const VALID_DATA_TRANSFER_TYPE = 'Files';
export const ACTIVE_DISCUSSION_SOURCE_TYPES = {
pin: 'pin',
discussion: 'discussion',
url: 'url',
};
export const DESIGN_DETAIL_LAYOUT_CLASSLIST = ['design-detail-layout', 'overflow-hidden', 'm-0'];
......@@ -19,6 +19,8 @@ import {
extractDiscussions,
extractDesign,
updateImageDiffNoteOptimisticResponse,
toDiffNoteGid,
extractDesignNoteId,
} from '../../utils/design_management_utils';
import {
updateStoreAfterAddImageDiffNote,
......@@ -145,8 +147,11 @@ export default {
mounted() {
Mousetrap.bind('esc', this.closeDesign);
this.trackEvent();
// We need to reset the active discussion when opening a new design
this.updateActiveDiscussion();
// Set active discussion immediately.
// This will ensure that, if a note is specified in the URL hash,
// the browser will scroll to, and highlight, the note in the UI
this.updateActiveDiscussionFromUrl();
},
beforeDestroy() {
Mousetrap.unbind('esc', this.closeDesign);
......@@ -266,15 +271,20 @@ export default {
this.isLatestVersion,
);
},
updateActiveDiscussion(id) {
updateActiveDiscussion(id, source = ACTIVE_DISCUSSION_SOURCE_TYPES.discussion) {
this.$apollo.mutate({
mutation: updateActiveDiscussionMutation,
variables: {
id,
source: ACTIVE_DISCUSSION_SOURCE_TYPES.discussion,
source,
},
});
},
updateActiveDiscussionFromUrl() {
const noteId = extractDesignNoteId(this.$route.hash);
const diffNoteGid = noteId ? toDiffNoteGid(noteId) : undefined;
return this.updateActiveDiscussion(diffNoteGid, ACTIVE_DISCUSSION_SOURCE_TYPES.url);
},
toggleResolvedComments() {
this.resolvedDiscussionsExpanded = !this.resolvedDiscussionsExpanded;
},
......
......@@ -34,6 +34,17 @@ export const extractDesigns = data => data.project.issue.designCollection.design
export const extractDesign = data => (extractDesigns(data) || [])[0];
export const toDiffNoteGid = noteId => `gid://gitlab/DiffNote/${noteId}`;
/**
* Return the note ID from a URL hash parameter
* @param {String} urlHash URL hash, including `#` prefix
*/
export const extractDesignNoteId = urlHash => {
const [, noteId] = urlHash.match('#note_([0-9]+$)') || [];
return noteId || null;
};
/**
* Generates optimistic response for a design upload mutation
* @param {Array<File>} files
......
---
title: Track unique web ide edit action for usage ping
merge_request: 40246
author:
type: changed
---
title: Ensure design comment is highlighted when comment is in URL
merge_request: 40477
author:
type: fixed
---
title: Bump doorkeeper to 5.1.1
merge_request: 40546
author:
type: changed
......@@ -405,7 +405,7 @@ To configure the Gitaly server:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the GitLab Rails application setup
gitaly['auth_token'] = 'gitlaysecret'
gitaly['auth_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
# Avoid running unnecessary services on the Gitaly server
......@@ -586,7 +586,7 @@ On each node perform the following:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the Gitaly setup
gitlab_rails['gitaly_token'] = 'gitalyecret'
gitlab_rails['gitaly_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
git_data_dirs({
......
......@@ -1131,7 +1131,7 @@ On each node:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the GitLab Rails application setup
gitaly['auth_token'] = 'gitlaysecret'
gitaly['auth_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
# Avoid running unnecessary services on the Gitaly server
......@@ -1474,7 +1474,7 @@ On each node perform the following:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the Gitaly setup
gitlab_rails['gitaly_token'] = 'gitalyecret'
gitlab_rails['gitaly_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
git_data_dirs({
......
......@@ -1130,7 +1130,7 @@ On each node:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the GitLab Rails application setup
gitaly['auth_token'] = 'gitlaysecret'
gitaly['auth_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
# Avoid running unnecessary services on the Gitaly server
......@@ -1473,7 +1473,7 @@ On each node perform the following:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the Gitaly setup
gitlab_rails['gitaly_token'] = 'gitalyecret'
gitlab_rails['gitaly_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
git_data_dirs({
......
......@@ -11,9 +11,9 @@ GitLab offers a [continuous integration](https://about.gitlab.com/stages-devops-
[pipeline](../pipelines/index.md), you must:
- Add a [`.gitlab-ci.yml` file](#creating-a-gitlab-ciyml-file) to your repository's root directory.
- Ensure your project is configured to use a [Runner](#configuring-a-runner).
- Ensure your project is configured to use a [runner](#configuring-a-runner).
The `.gitlab-ci.yml` file tells the GitLab Runner what to do. A simple pipeline commonly has
The `.gitlab-ci.yml` file tells the runner what to do. A simple pipeline commonly has
three [stages](../yaml/README.md#stages):
- `build`
......@@ -57,7 +57,7 @@ The `.gitlab-ci.yml` file is where you configure what CI does with your project.
It lives in the root of your repository.
On any push to your repository, GitLab will look for the `.gitlab-ci.yml`
file and start jobs on _Runners_ according to the contents of the file,
file and start jobs on _runners_ according to the contents of the file,
for that commit.
Because `.gitlab-ci.yml` is in the repository and is version controlled, old
......@@ -109,7 +109,7 @@ The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when
they should be run. The jobs are defined as top-level elements with a name (in
our case `rspec` and `rubocop`) and always have to contain the `script` keyword.
Jobs are used to create jobs, which are then picked by
[Runners](../runners/README.md) and executed within the environment of the Runner.
[runners](../runners/README.md) and executed within the environment of the runner.
What is important is that each job is run independently from each other.
......@@ -148,59 +148,54 @@ Clicking on it you will be directed to the jobs page for that specific commit.
![Single commit jobs page](img/single_commit_status_pending.png)
Notice that there is a pending job which is named after what we wrote in
`.gitlab-ci.yml`. "stuck" indicates that there is no Runner configured
`.gitlab-ci.yml`. "stuck" indicates that there is no runner configured
yet for this job.
The next step is to configure a Runner so that it picks the pending jobs.
The next step is to configure a runner so that it picks the pending jobs.
## Configuring a Runner
## Configuring a runner
In GitLab, Runners run the jobs that you define in `.gitlab-ci.yml`. A Runner
can be a virtual machine, a VPS, a bare-metal machine, a Docker container or
even a cluster of containers. GitLab and the Runners communicate through an API,
so the only requirement is that the Runner's machine has network access to the
In GitLab, runners run the jobs that you define in `.gitlab-ci.yml`. A runner
can be a virtual machine, a VPS, a bare-metal machine, a Docker container, or
even a cluster of containers. GitLab and the runner communicate through an API,
so the only requirement is that the runner's machine has network access to the
GitLab server.
A Runner can be specific to a certain project or serve multiple projects in
GitLab. If it serves all projects it's called a _Shared Runner_.
A runner can be specific to a certain project or serve multiple projects in
GitLab. If it serves all projects, it's called a _shared runner_.
Find more information about different Runners in the
[Runners](../runners/README.md) documentation.
Find more information about runners in the
[runner](../runners/README.md) documentation.
You can find whether any Runners are assigned to your project by going to
**Settings ➔ CI/CD**. Setting up a Runner is easy and straightforward. The
official Runner supported by GitLab is written in Go and its documentation
can be found at <https://docs.gitlab.com/runner/>.
The official runner supported by GitLab is written in Go.
View [the documentation](https://docs.gitlab.com/runner/).
In order to have a functional Runner you need to follow two steps:
For a runner to be available in GitLab, you must:
1. [Install it](https://docs.gitlab.com/runner/install/)
1. [Configure it](https://docs.gitlab.com/runner/configuration/)
1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
1. [Register a runner for your group or project](https://docs.gitlab.com/runner/register/).
Follow the links above to set up your own Runner or use a Shared Runner as
described in the next section.
Once the Runner has been set up, you should see it on the Runners page of your
project, following **Settings ➔ CI/CD**.
When a runner is available, you can view it by
clicking **Settings > CI/CD** and expanding **Runners**.
![Activated runners](img/runners_activated.png)
### Shared Runners
### Shared runners
If you use [GitLab.com](https://gitlab.com/) you can use the **Shared Runners**
provided by GitLab Inc.
If you use [GitLab.com](https://gitlab.com/), you can use the **shared runners**
provided by GitLab.
These are special virtual machines that run on GitLab's infrastructure and can
build any project.
To enable the **Shared Runners** you have to go to your project's
**Settings CI/CD** and click **Enable shared runners**.
To enable shared runners, go to your project's or group's
**Settings > CI/CD** and click **Enable shared runners**.
[Read more on Shared Runners](../runners/README.md).
[Read more about shared runners](../runners/README.md#shared-runners).
## Seeing the status of your pipeline and jobs
## Viewing the status of your pipeline and jobs
After configuring the Runner successfully, you should see the status of your
After configuring the runner successfully, you should see the status of your
last commit change from _pending_ to either _running_, _success_ or _failed_.
You can view all pipelines by going to the **Pipelines** page in your project.
......
......@@ -64,6 +64,65 @@ users have to fix their `.gitlab-ci.yml` that could annoy their workflow.
Please read [versioning](#versioning) section for introducing breaking change safely.
## Versioning
Versioning allows you to introduce a new template without modifying the existing
one. This process is useful when we need to introduce a breaking change,
but don't want to affect the existing projects that depends on the current template.
### Stable version
A stable CI/CD template is a template that only introduces breaking changes in major
release milestones. Name the stable version of a template as `<template-name>.gitlab-ci.yml`,
for example `Jobs/Deploy.gitlab-ci.yml`.
You can make a new stable template by copying [the latest template](#latest-version)
available in a major milestone release of GitLab like `13.0`. All breaking changes
must be announced in a blog post before the official release, for example
[GitLab.com is moving to 13.0, with narrow breaking changes](https://about.gitlab.com/releases/2020/05/06/gitlab-com-13-0-breaking-changes/)
You can change a stable template version in a minor GitLab release like `13.1` if:
- The change is not a [breaking change](#backward-compatibility).
- The change is ported to [the latest template](#latest-version), if one exists.
### Latest version
Templates marked as `latest` can be updated in any release, even with
[breaking changes](#backward-compatibility). Add `.latest` to the template name if
it's considered the latest version, for example `Jobs/Deploy.latest.gitlab-ci.yml`.
When you introduce [a breaking change](#backward-compatibility),
you **must** test and document [the upgrade path](#verify-breaking-changes).
In general, we should not promote the latest template as the best option, as it could surprise users with unexpected problems.
If the `latest` template does not exist yet, you can copy [the stable template](#stable-version).
### How to include an older stable template
Users may want to use an older [stable template](#stable-version) that is not bundled
in the current GitLab package. For example, the stable templates in GitLab v13.0 and
GitLab v14.0 could be so different that a user will want to continue using the v13.0 template even
after upgrading to GitLab 14.0.
You can add a note in the template or in documentation explaining how to use `include:remote`
to include older template versions:
```yaml
# To use the v13 stable template, which is not included in v14, fetch the specifc
# template from the remote template repository with the `include:remote:` keyword.
# If you fetch from the GitLab canonical project, use the following URL format:
# https://gitlab.com/gitlab-org/gitlab/-/raw/<version>/lib/gitlab/ci/templates/<template-name>
include:
remote: https://gitlab.com/gitlab-org/gitlab/-/raw/v13.0.1-ee/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
```
### Further reading
There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) about
introducing versioning concepts in GitLab CI Templates. You can check that issue to
follow the progress.
## Testing
Each CI/CD template must be tested in order to make sure that it's safe to be published.
......@@ -95,18 +154,20 @@ You should write an RSpec test to make sure that pipeline jobs will be generated
1. Add a test file at `spec/lib/gitlab/ci/templates/<template-category>/<template-name>_spec.rb`
1. Test that pipeline jobs are properly created via `Ci::CreatePipelineService`.
### Verify breaking changes
When you introduce a breaking change to [a `latest` template](#latest-version),
you must:
1. Test the upgrade path from [the stable template](#stable-version).
1. Verify what kind of errors users will encounter.
1. Document it as a troubleshooting guide.
This information will be important for users when [a stable template](#stable-version)
is updated in a major version GitLab release.
## Security
A template could contain malicious code. For example, a template that contains the `export` shell command in a job
might accidentally expose project secret variables in a job log.
If you're unsure if it's secure or not, you need to ask security experts for cross-validation.
## Versioning
Versioning allows you to introduce a new template without modifying the existing
one. This is useful process especially when we need to introduce a breaking change,
but don't want to affect the existing projects that depends on the current template.
There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) for
introducing versioning concept in GitLab Ci Template. Please follow the issue for
checking the progress.
......@@ -20,7 +20,7 @@ NOTE: **Note:**
This option is available only if your GitLab administrator has
[enabled support for the Maven repository](../../../administration/packages/index.md).
After the Packages feature is enabled, the Maven Repository will be available for
After the Packages feature is enabled, the Maven Repository is available for
all new projects by default. To enable it for existing projects, or if you want
to disable it:
......@@ -34,7 +34,7 @@ repository.
## Getting Started with Maven
This section will cover installing Maven and building a package. This is a
This section covers installing Maven and building a package. This is a
quickstart to help if you're new to building Maven packages. If you're already
using Maven and understand how to build your own packages, move onto the
[next section](#adding-the-gitlab-package-registry-as-a-maven-remote).
......@@ -107,7 +107,7 @@ You should see a new directory where you ran this command matching your
## Getting started with Gradle
This section will cover installing Gradle and initializing a Java project. This is a
This section covers installing Gradle and initializing a Java project. This is a
quickstart to help if you're new to Gradle. If you're already
using Gradle and understand how to build your own packages, move onto the
[next section](#adding-the-gitlab-package-registry-as-a-maven-remote).
......@@ -128,7 +128,7 @@ If you want to use an existing Gradle project, installation is not necessary.
Simply execute `gradlew` (on Linux) or `gradlew.bat` (on Windows) in the project
directory instead.
You should see something imilar to the below printed in the output:
You should see something similar to the below printed in the output:
```plaintext
------------------------------------------------------------
......@@ -191,7 +191,7 @@ Select build script DSL:
Enter selection (default: Groovy) [1..2]
```
Choose `1` to create a new Java Library project which will be described in Groovy DSL. The output should be:
Choose `1` to create a new Java Library project which is described in Groovy DSL. The output should be:
```plaintext
Select test framework:
......@@ -213,7 +213,7 @@ Enter a project name or hit enter to use the directory name as project name.
The next step is to add the GitLab Package Registry as a Maven remote. If a
project is private or you want to upload Maven artifacts to GitLab,
credentials will need to be provided for authorization too. Support is available
credentials must be provided for authorization too. Support is available
for [personal access tokens](#authenticating-with-a-personal-access-token),
[CI job tokens](#authenticating-with-a-ci-job-token), and
[deploy tokens](../../project/deploy_tokens/index.md) only. Regular username/password
......@@ -388,7 +388,7 @@ repositories {
To download and upload packages from GitLab, you need a `repository` and
`distributionManagement` section in your `pom.xml` file. If you're following the
steps from above, then you'll need to add the following information to your
steps from above, then you must add the following information to your
`my-project/pom.xml` file.
Depending on your workflow and the amount of Maven packages you have, there are
......@@ -462,13 +462,13 @@ project's ID can be used for uploading.
If you rely on many packages, it might be inefficient to include the `repository` section
with a unique URL for each package. Instead, you can use the group level endpoint for
all your Maven packages stored within one GitLab group. Only packages you have access to
will be available for download.
are available for download.
The group level endpoint works with any package names, which means the you
have the flexibility of naming compared to [instance level endpoint](#instance-level-maven-endpoint).
However, GitLab will not guarantee the uniqueness of the package names within
However, GitLab does not guarantee the uniqueness of the package names within
the group. You can have two projects with the same package name and package
version. As a result, GitLab will serve whichever one is more recent.
version. As a result, GitLab serves whichever one is more recent.
The example below shows how the relevant `repository` section of your `pom.xml`
would look like. You still need a project specific URL for uploading a package in
......@@ -524,7 +524,7 @@ For retrieving artifacts, you can use either the
If you rely on many packages, it might be inefficient to include the `repository` section
with a unique URL for each package. Instead, you can use the instance level endpoint for
all maven packages stored in GitLab and the packages you have access to will be available
all maven packages stored in GitLab and the packages you have access to are available
for download.
Note that **only packages that have the same path as the project** are exposed via
......@@ -662,7 +662,7 @@ artifacts or even delete them.
Installing a package from the GitLab Package Registry requires that you set up
the [remote and authentication](#adding-the-gitlab-package-registry-as-a-maven-remote)
as above. Once this is completed, there are two ways for installaing a package.
as above. Once this is completed, there are two ways to install a package.
### Install using Maven with `mvn install`
......@@ -732,7 +732,7 @@ you can configure GitLab CI/CD to build new packages automatically.
The example below shows how to create a new package each time the `master` branch
is updated:
1. Create a `ci_settings.xml` file that will serve as Maven's `settings.xml` file.
1. Create a `ci_settings.xml` file that serves as Maven's `settings.xml` file.
Add the server section with the same ID you defined in your `pom.xml` file.
For example, in our case it's `gitlab-maven`:
......@@ -792,9 +792,9 @@ is updated:
1. Push those files to your repository.
The next time the `deploy` job runs, it will copy `ci_settings.xml` to the
The next time the `deploy` job runs, it copies `ci_settings.xml` to the
user's home location (in this case the user is `root` since it runs in a
Docker container), and Maven will utilize the configured CI
Docker container), and Maven uses the configured CI
[environment variables](../../../ci/variables/README.md#predefined-environment-variables).
### Creating Maven packages with GitLab CI/CD using Gradle
......
......@@ -136,7 +136,10 @@ module API
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count if find_user_from_warden
if find_user_from_warden
Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user)
end
present commit_detail, with: Entities::CommitDetail, stats: params[:stats]
else
......
......@@ -5,10 +5,10 @@ require 'spec_helper'
RSpec.describe Oauth::TokenInfoController do
describe '#show' do
context 'when the user is not authenticated' do
it 'responds with a 401' do
it 'responds with a 400' do
get :show
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
......@@ -36,10 +36,10 @@ RSpec.describe Oauth::TokenInfoController do
end
context 'when the doorkeeper_token is not recognised' do
it 'responds with a 401' do
it 'responds with a 400' do
get :show, params: { access_token: 'unknown_token' }
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
......@@ -49,10 +49,10 @@ RSpec.describe Oauth::TokenInfoController do
create(:oauth_access_token, created_at: 2.days.ago, expires_in: 10.minutes)
end
it 'responds with a 401' do
it 'responds with a 400' do
get :show, params: { access_token: access_token.token }
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
......@@ -60,10 +60,10 @@ RSpec.describe Oauth::TokenInfoController do
context 'when the token is revoked' do
let(:access_token) { create(:oauth_access_token, revoked_at: 2.days.ago) }
it 'responds with a 401' do
it 'responds with a 400' do
get :show, params: { access_token: access_token.token }
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
......
......@@ -91,7 +91,9 @@ describe('Design note component', () => {
note,
});
expect(scrollIntoViewMock).toHaveBeenCalled();
return wrapper.vm.$nextTick().then(() => {
expect(scrollIntoViewMock).toHaveBeenCalled();
});
});
it('should not render edit icon when user does not have a permission', () => {
......
......@@ -57,7 +57,7 @@ exports[`Design management design index page renders design index 1`] = `
<design-discussion-stub
data-testid="unresolved-discussion"
designid="test"
designid="design-id"
discussion="[object Object]"
discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
......@@ -105,7 +105,7 @@ exports[`Design management design index page renders design index 1`] = `
>
<design-discussion-stub
data-testid="resolved-discussion"
designid="test"
designid="design-id"
discussion="[object Object]"
discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
......
......@@ -7,18 +7,19 @@ import DesignIndex from '~/design_management/pages/design/index.vue';
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
import DesignPresentation from '~/design_management/components/design_presentation.vue';
import createImageDiffNoteMutation from '~/design_management/graphql/mutations/create_image_diff_note.mutation.graphql';
import design from '../../mock_data/design';
import mockResponseWithDesigns from '../../mock_data/designs';
import mockResponseNoDesigns from '../../mock_data/no_designs';
import mockAllVersions from '../../mock_data/all_versions';
import updateActiveDiscussion from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
import {
DESIGN_NOT_FOUND_ERROR,
DESIGN_VERSION_NOT_EXIST_ERROR,
} from '~/design_management/utils/error_messages';
import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
import { DESIGNS_ROUTE_NAME, DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
import createRouter from '~/design_management/router';
import * as utils from '~/design_management/utils/design_management_utils';
import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants';
import design from '../../mock_data/design';
import mockResponseWithDesigns from '../../mock_data/designs';
import mockResponseNoDesigns from '../../mock_data/no_designs';
import mockAllVersions from '../../mock_data/all_versions';
jest.mock('~/flash');
jest.mock('mousetrap', () => ({
......@@ -34,6 +35,12 @@ const DesignReplyForm = {
focusInput,
},
};
const mockDesignNoDiscussions = {
...design,
discussions: {
nodes: [],
},
};
const localVue = createLocalVue();
localVue.use(VueRouter);
......@@ -75,7 +82,7 @@ describe('Design management design index page', () => {
const findSidebar = () => wrapper.find(DesignSidebar);
const findDesignPresentation = () => wrapper.find(DesignPresentation);
function createComponent(loading = false, data = {}) {
function createComponent({ loading = false } = {}, { data = {}, intialRouteOptions = {} } = {}) {
const $apollo = {
queries: {
design: {
......@@ -87,6 +94,8 @@ describe('Design management design index page', () => {
router = createRouter();
router.push({ name: DESIGN_ROUTE_NAME, params: { id: design.id }, ...intialRouteOptions });
wrapper = shallowMount(DesignIndex, {
propsData: { id: '1' },
mocks: { $apollo },
......@@ -126,29 +135,28 @@ describe('Design management design index page', () => {
},
};
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl);
createComponent(true);
createComponent({ loading: true });
wrapper.vm.$router.push('/designs/test');
expect(mockEl.classList.add).toHaveBeenCalledTimes(1);
expect(mockEl.classList.add).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
});
});
it('sets loading state', () => {
createComponent(true);
createComponent({ loading: true });
expect(wrapper.element).toMatchSnapshot();
});
it('renders design index', () => {
createComponent(false, { design });
createComponent({ loading: false }, { data: { design } });
expect(wrapper.element).toMatchSnapshot();
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
it('passes correct props to sidebar component', () => {
createComponent(false, { design });
createComponent({ loading: false }, { data: { design } });
expect(findSidebar().props()).toEqual({
design,
......@@ -158,14 +166,14 @@ describe('Design management design index page', () => {
});
it('opens a new discussion form', () => {
createComponent(false, {
design: {
...design,
discussions: {
nodes: [],
createComponent(
{ loading: false },
{
data: {
design,
},
},
});
);
findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 });
......@@ -175,15 +183,15 @@ describe('Design management design index page', () => {
});
it('keeps new discussion form focused', () => {
createComponent(false, {
design: {
...design,
discussions: {
nodes: [],
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
},
},
annotationCoordinates,
});
);
findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 });
......@@ -191,16 +199,16 @@ describe('Design management design index page', () => {
});
it('sends a mutation on submitting form and closes form', () => {
createComponent(false, {
design: {
...design,
discussions: {
nodes: [],
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
comment: newComment,
},
},
annotationCoordinates,
comment: newComment,
});
);
findDiscussionForm().vm.$emit('submitForm');
expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables);
......@@ -216,16 +224,16 @@ describe('Design management design index page', () => {
});
it('closes the form and clears the comment on canceling form', () => {
createComponent(false, {
design: {
...design,
discussions: {
nodes: [],
createComponent(
{ loading: false },
{
data: {
design,
annotationCoordinates,
comment: newComment,
},
},
annotationCoordinates,
comment: newComment,
});
);
findDiscussionForm().vm.$emit('cancelForm');
......@@ -238,15 +246,15 @@ describe('Design management design index page', () => {
describe('with error', () => {
beforeEach(() => {
createComponent(false, {
design: {
...design,
discussions: {
nodes: [],
createComponent(
{ loading: false },
{
data: {
design: mockDesignNoDiscussions,
errorMessage: 'woops',
},
},
errorMessage: 'woops',
});
);
});
it('GlAlert is rendered in correct position with correct content', () => {
......@@ -257,7 +265,7 @@ describe('Design management design index page', () => {
describe('onDesignQueryResult', () => {
describe('with no designs', () => {
it('redirects to /designs', () => {
createComponent(true);
createComponent({ loading: true });
router.push = jest.fn();
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
......@@ -272,7 +280,7 @@ describe('Design management design index page', () => {
describe('when no design exists for given version', () => {
it('redirects to /designs', () => {
createComponent(true);
createComponent({ loading: true });
wrapper.setData({
allVersions: mockAllVersions,
});
......@@ -291,4 +299,24 @@ describe('Design management design index page', () => {
});
});
});
describe('when hash present in current route', () => {
it('calls updateActiveDiscussion mutation', () => {
createComponent(
{ loading: false },
{
data: {
design,
},
intialRouteOptions: { hash: '#note_123' },
},
);
expect(mutate).toHaveBeenCalledTimes(1);
expect(mutate).toHaveBeenCalledWith({
mutation: updateActiveDiscussion,
variables: { id: 'gid://gitlab/DiffNote/123', source: 'url' },
});
});
});
});
......@@ -579,7 +579,9 @@ describe('Design management index page', () => {
});
createComponent(true);
expect(scrollIntoViewMock).toHaveBeenCalled();
return wrapper.vm.$nextTick().then(() => {
expect(scrollIntoViewMock).toHaveBeenCalled();
});
});
});
});
......@@ -6,6 +6,7 @@ import {
updateImageDiffNoteOptimisticResponse,
isValidDesignFile,
extractDesign,
extractDesignNoteId,
} from '~/design_management/utils/design_management_utils';
import mockResponseNoDesigns from '../mock_data/no_designs';
import mockResponseWithDesigns from '../mock_data/designs';
......@@ -171,3 +172,19 @@ describe('extractDesign', () => {
});
});
});
describe('extractDesignNoteId', () => {
it.each`
hash | expectedNoteId
${'#note_0'} | ${'0'}
${'#note_1'} | ${'1'}
${'#note_23'} | ${'23'}
${'#note_456'} | ${'456'}
${'note_1'} | ${null}
${'#note_'} | ${null}
${'#note_asd'} | ${null}
${'#note_1asd'} | ${null}
`('returns $expectedNoteId when hash is $hash', ({ hash, expectedNoteId }) => {
expect(extractDesignNoteId(hash)).toBe(expectedNoteId);
});
});
......@@ -367,10 +367,31 @@ RSpec.describe API::Commits do
end
end
it 'does not increment the usage counters using access token authentication' do
expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
context 'when using access token authentication' do
it 'does not increment the usage counters' do
expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_web_ide_edit_action)
post api(url, user), params: valid_c_params
post api(url, user), params: valid_c_params
end
end
context 'when using warden' do
it 'increments usage counters', :clean_gitlab_redis_shared_state do
session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] }
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end
cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
post api(url), params: valid_c_params
end
end
context 'a new file in project repo' do
......
......@@ -20,7 +20,7 @@ RSpec.describe 'OAuth tokens' do
request_oauth_token(user, client_basic_auth_header(client))
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('invalid_grant')
end
end
......@@ -62,7 +62,7 @@ RSpec.describe 'OAuth tokens' do
request_oauth_token(user, basic_auth_header(client.uid, 'invalid secret'))
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('invalid_client')
end
end
......@@ -72,7 +72,7 @@ RSpec.describe 'OAuth tokens' do
shared_examples 'does not create an access token' do
let(:user) { create(:user) }
it { expect(response).to have_gitlab_http_status(:unauthorized) }
it { expect(response).to have_gitlab_http_status(:bad_request) }
end
context 'when user is blocked' do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册