Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
429d1abad2
commit
8c30d396c5
18 changed files with 148 additions and 84 deletions
|
@ -15,7 +15,7 @@ variables:
|
||||||
RAILS_ENV: "test"
|
RAILS_ENV: "test"
|
||||||
NODE_ENV: "test"
|
NODE_ENV: "test"
|
||||||
SIMPLECOV: "true"
|
SIMPLECOV: "true"
|
||||||
GIT_DEPTH: "20"
|
GIT_DEPTH: "50"
|
||||||
GIT_SUBMODULE_STRATEGY: "none"
|
GIT_SUBMODULE_STRATEGY: "none"
|
||||||
GET_SOURCES_ATTEMPTS: "3"
|
GET_SOURCES_ATTEMPTS: "3"
|
||||||
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/report-master.json
|
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/report-master.json
|
||||||
|
|
|
@ -72,5 +72,3 @@ schedule:package-and-qa:
|
||||||
- .only-code-qa-changes
|
- .only-code-qa-changes
|
||||||
- .only-canonical-schedules
|
- .only-canonical-schedules
|
||||||
needs: ["build-qa-image", "gitlab:assets:compile"]
|
needs: ["build-qa-image", "gitlab:assets:compile"]
|
||||||
# Allowed to fail until https://gitlab.com/gitlab-org/gitlab/issues/33272 is fixed.
|
|
||||||
allow_failure: true
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ export default class LabelsSelect {
|
||||||
$selectbox,
|
$selectbox,
|
||||||
$sidebarCollapsedValue,
|
$sidebarCollapsedValue,
|
||||||
$value,
|
$value,
|
||||||
$dropdownMenu,
|
|
||||||
abilityName,
|
abilityName,
|
||||||
defaultLabel,
|
defaultLabel,
|
||||||
issueUpdateURL,
|
issueUpdateURL,
|
||||||
|
@ -68,7 +67,6 @@ export default class LabelsSelect {
|
||||||
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
|
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
|
||||||
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
|
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
|
||||||
$value = $block.find('.value');
|
$value = $block.find('.value');
|
||||||
$dropdownMenu = $dropdown.parent().find('.dropdown-menu');
|
|
||||||
$loading = $block.find('.block-loading').fadeOut();
|
$loading = $block.find('.block-loading').fadeOut();
|
||||||
fieldName = $dropdown.data('fieldName');
|
fieldName = $dropdown.data('fieldName');
|
||||||
initialSelected = $selectbox
|
initialSelected = $selectbox
|
||||||
|
@ -456,21 +454,9 @@ export default class LabelsSelect {
|
||||||
}
|
}
|
||||||
|
|
||||||
$loading.fadeIn();
|
$loading.fadeIn();
|
||||||
const oldLabels = boardsStore.detail.issue.labels;
|
|
||||||
|
|
||||||
boardsStore.detail.issue
|
boardsStore.detail.issue
|
||||||
.update($dropdown.attr('data-issue-update'))
|
.update($dropdown.attr('data-issue-update'))
|
||||||
.then(() => {
|
|
||||||
if (isScopedLabel(label)) {
|
|
||||||
const prevIds = oldLabels.map(label => label.id);
|
|
||||||
const newIds = boardsStore.detail.issue.labels.map(label => label.id);
|
|
||||||
const differentIds = _.difference(prevIds, newIds);
|
|
||||||
$dropdown.data('marked', newIds);
|
|
||||||
$dropdownMenu
|
|
||||||
.find(differentIds.map(id => `[data-label-id="${id}"]`).join(','))
|
|
||||||
.removeClass('is-active');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(fadeOutLoader)
|
.then(fadeOutLoader)
|
||||||
.catch(fadeOutLoader);
|
.catch(fadeOutLoader);
|
||||||
} else if (handleClick) {
|
} else if (handleClick) {
|
||||||
|
|
|
@ -72,6 +72,8 @@ class Projects::CommitsController < Projects::ApplicationController
|
||||||
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
|
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@commits.each(&:lazy_author) # preload authors
|
||||||
|
|
||||||
@commits = @commits.with_latest_pipeline(@ref)
|
@commits = @commits.with_latest_pipeline(@ref)
|
||||||
@commits = set_commits_for_rendering(@commits)
|
@commits = set_commits_for_rendering(@commits)
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,8 +65,20 @@ class TodosFinder
|
||||||
params[:action_id]
|
params[:action_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def action_array_provided?
|
||||||
|
params[:action].is_a?(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
def map_actions_to_ids
|
||||||
|
params[:action].map { |item| Todo::ACTION_NAMES.key(item.to_sym) }
|
||||||
|
end
|
||||||
|
|
||||||
def to_action_id
|
def to_action_id
|
||||||
Todo::ACTION_NAMES.key(action.to_sym)
|
if action_array_provided?
|
||||||
|
map_actions_to_ids
|
||||||
|
else
|
||||||
|
Todo::ACTION_NAMES.key(action.to_sym)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def action?
|
def action?
|
||||||
|
@ -133,9 +145,19 @@ class TodosFinder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def action_id_array_provided?
|
||||||
|
params[:action_id].is_a?(Array) && params[:action_id].any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def by_action_ids(items)
|
||||||
|
items.for_action(action_id)
|
||||||
|
end
|
||||||
|
|
||||||
def by_action_id(items)
|
def by_action_id(items)
|
||||||
|
return by_action_ids(items) if action_id_array_provided?
|
||||||
|
|
||||||
if action_id?
|
if action_id?
|
||||||
items.for_action(action_id)
|
by_action_ids(items)
|
||||||
else
|
else
|
||||||
items
|
items
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,16 +56,6 @@ module AvatarsHelper
|
||||||
}))
|
}))
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_avatar_url_for(only_path: true, **options)
|
|
||||||
if options[:url]
|
|
||||||
options[:url]
|
|
||||||
elsif options[:user]
|
|
||||||
avatar_icon_for_user(options[:user], options[:size], only_path: only_path)
|
|
||||||
else
|
|
||||||
avatar_icon_for_email(options[:user_email], options[:size], only_path: only_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_avatar_without_link(options = {})
|
def user_avatar_without_link(options = {})
|
||||||
avatar_size = options[:size] || 16
|
avatar_size = options[:size] || 16
|
||||||
user_name = options[:user].try(:name) || options[:user_name]
|
user_name = options[:user].try(:name) || options[:user_name]
|
||||||
|
@ -111,6 +101,19 @@ module AvatarsHelper
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def user_avatar_url_for(only_path: true, **options)
|
||||||
|
return options[:url] if options[:url]
|
||||||
|
|
||||||
|
email = options[:user_email]
|
||||||
|
user = options.key?(:user) ? options[:user] : User.find_by_any_email(email)
|
||||||
|
|
||||||
|
if user
|
||||||
|
avatar_icon_for_user(user, options[:size], only_path: only_path)
|
||||||
|
else
|
||||||
|
gravatar_icon(email, options[:size])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def source_icon(source, options = {})
|
def source_icon(source, options = {})
|
||||||
avatar_url = source.try(:avatar_url)
|
avatar_url = source.try(:avatar_url)
|
||||||
|
|
||||||
|
|
|
@ -257,10 +257,9 @@ class Commit
|
||||||
end
|
end
|
||||||
|
|
||||||
def author
|
def author
|
||||||
# We use __sync so that we get the actual objects back (including an actual
|
strong_memoize(:author) do
|
||||||
# nil), instead of a wrapper, as returning a wrapped nil breaks a lot of
|
lazy_author&.itself
|
||||||
# code.
|
end
|
||||||
lazy_author.__sync
|
|
||||||
end
|
end
|
||||||
request_cache(:author) { author_email.downcase }
|
request_cache(:author) { author_email.downcase }
|
||||||
|
|
||||||
|
|
5
changelogs/unreleased/12564-email-regex.yml
Normal file
5
changelogs/unreleased/12564-email-regex.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Adjust placeholder to solve misleading regex
|
||||||
|
merge_request: 18235
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Optimize SQL requests for BlameController and CommitsController
|
||||||
|
merge_request: 18342
|
||||||
|
author:
|
||||||
|
type: performance
|
|
@ -3,6 +3,19 @@
|
||||||
NOTE: **Note:** Praefect is an experimental service, and for testing purposes only at
|
NOTE: **Note:** Praefect is an experimental service, and for testing purposes only at
|
||||||
this time.
|
this time.
|
||||||
|
|
||||||
|
Praefect is an optional reverse-proxy for [Gitaly](../index.md) to manage a
|
||||||
|
cluster of Gitaly nodes for high availability through replication.
|
||||||
|
If a Gitaly node becomes unavailable, it will be possible to fail over to a
|
||||||
|
warm Gitaly replica.
|
||||||
|
|
||||||
|
The first minimal version will support:
|
||||||
|
|
||||||
|
- Eventual consistency of the secondary replicas.
|
||||||
|
- Manual fail over from the primary to the secondary.
|
||||||
|
|
||||||
|
Follow the [HA Gitaly epic](https://gitlab.com/groups/gitlab-org/-/epics/1489)
|
||||||
|
for updates and roadmap.
|
||||||
|
|
||||||
## Omnibus
|
## Omnibus
|
||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
|
|
|
@ -58,7 +58,7 @@ each pipeline includes the following [variables](../ci/variables/README.md):
|
||||||
- `RAILS_ENV: "test"`
|
- `RAILS_ENV: "test"`
|
||||||
- `NODE_ENV: "test"`
|
- `NODE_ENV: "test"`
|
||||||
- `SIMPLECOV: "true"`
|
- `SIMPLECOV: "true"`
|
||||||
- `GIT_DEPTH: "20"`
|
- `GIT_DEPTH: "50"`
|
||||||
- `GIT_SUBMODULE_STRATEGY: "none"`
|
- `GIT_SUBMODULE_STRATEGY: "none"`
|
||||||
- `GET_SOURCES_ATTEMPTS: "3"`
|
- `GET_SOURCES_ATTEMPTS: "3"`
|
||||||
- `KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json`
|
- `KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json`
|
||||||
|
|
|
@ -83,20 +83,6 @@ The minimum key size is 1024 bits, defaulting to 2048. If you wish to generate a
|
||||||
stronger RSA key pair, specify the `-b` flag with a higher bit value than the
|
stronger RSA key pair, specify the `-b` flag with a higher bit value than the
|
||||||
default.
|
default.
|
||||||
|
|
||||||
The old, default password encoding for SSH private keys is
|
|
||||||
[insecure](https://latacora.micro.blog/the-default-openssh/);
|
|
||||||
it's only a single round of an MD5 hash. Since OpenSSH version 6.5, you should
|
|
||||||
use the `-o` option to `ssh-keygen` to encode your private key in a new, more
|
|
||||||
secure format.
|
|
||||||
|
|
||||||
If you already have an RSA SSH key pair to use with GitLab, consider upgrading it
|
|
||||||
to use the more secure password encryption format by using the following command
|
|
||||||
on the private key:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh-keygen -o -f ~/.ssh/id_rsa
|
|
||||||
```
|
|
||||||
|
|
||||||
## Generating a new SSH key pair
|
## Generating a new SSH key pair
|
||||||
|
|
||||||
Before creating an SSH key pair, make sure to understand the
|
Before creating an SSH key pair, make sure to understand the
|
||||||
|
@ -114,7 +100,7 @@ To create a new SSH key pair:
|
||||||
Or, if you want to use RSA:
|
Or, if you want to use RSA:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh-keygen -o -t rsa -b 4096 -C "email@example.com"
|
ssh-keygen -t rsa -b 4096 -C "email@example.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
The `-C` flag adds a comment in the key in case you have multiple of them
|
The `-C` flag adds a comment in the key in case you have multiple of them
|
||||||
|
@ -139,9 +125,31 @@ To create a new SSH key pair:
|
||||||
you can use the `-p` flag:
|
you can use the `-p` flag:
|
||||||
|
|
||||||
```
|
```
|
||||||
ssh-keygen -p -o -f <keyname>
|
ssh-keygen -p -f <keyname>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### OpenSSH < v7.8
|
||||||
|
|
||||||
|
Pre OpenSSH 7.8, default password encoding for SSH private keys was
|
||||||
|
[insecure](https://latacora.micro.blog/the-default-openssh/);
|
||||||
|
it's only a single round of an MD5 hash. For OpenSSH version 6.5 to version 7.8, you should
|
||||||
|
use the `-o` option to `ssh-keygen` to [encode your private key in a new, more
|
||||||
|
secure format.](https://superuser.com/questions/1455735/what-does-ssh-keygen-o-do#answer-1455738)
|
||||||
|
|
||||||
|
If you already have an RSA SSH key pair to use with GitLab, consider upgrading it
|
||||||
|
to use the more secure password encryption format by using the following command
|
||||||
|
on the private key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -o -f ~/.ssh/id_rsa
|
||||||
|
```
|
||||||
|
|
||||||
|
Or generate a new RSA key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -o -t rsa -b 4096 -C "email@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
Now, it's time to add the newly created public key to your GitLab account.
|
Now, it's time to add the newly created public key to your GitLab account.
|
||||||
|
|
||||||
## Adding an SSH key to your GitLab account
|
## Adding an SSH key to your GitLab account
|
||||||
|
|
|
@ -132,13 +132,17 @@ chart is used to install this application with a
|
||||||
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/ingress/values.yaml)
|
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/ingress/values.yaml)
|
||||||
file.
|
file.
|
||||||
|
|
||||||
#### Modsecurity Application Firewall
|
#### Web Application Firewall (ModSecurity)
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/65192) in GitLab 12.3 (enabled using `ingress_modsecurity` [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development)).
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/65192) in GitLab 12.3 (enabled using `ingress_modsecurity` [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development)).
|
||||||
|
|
||||||
GitLab supports
|
Out of the box, GitLab provides you real-time security monitoring with
|
||||||
[`modsecurity`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity)
|
[`modsecurity`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity)
|
||||||
to check requests against [OWASP's Core Rule Set](https://www.modsecurity.org/CRS/Documentation/).
|
|
||||||
|
Modsecurity is a toolkit for real-time web application monitoring, logging,
|
||||||
|
and access control. With GitLab's offering, the [OWASP's Core Rule Set](https://www.modsecurity.org/CRS/Documentation/), which provides generic attack detection capabilities,
|
||||||
|
is automatically applied.
|
||||||
|
|
||||||
This feature:
|
This feature:
|
||||||
|
|
||||||
- Runs in "Detection-only mode" unless configured otherwise.
|
- Runs in "Detection-only mode" unless configured otherwise.
|
||||||
|
|
|
@ -17,6 +17,7 @@ module Gitlab
|
||||||
i = 0
|
i = 0
|
||||||
blame.each do |commit, line|
|
blame.each do |commit, line|
|
||||||
commit = Commit.new(commit, project)
|
commit = Commit.new(commit, project)
|
||||||
|
commit.lazy_author # preload author
|
||||||
|
|
||||||
sha = commit.sha
|
sha = commit.sha
|
||||||
if prev_sha != sha
|
if prev_sha != sha
|
||||||
|
|
|
@ -6519,6 +6519,9 @@ msgstr ""
|
||||||
msgid "Everything you need to create a GitLab Pages site using plain HTML."
|
msgid "Everything you need to create a GitLab Pages site using plain HTML."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Example: @sub\\.company\\.com$"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Example: Usage = single query. (Requested) / (Capacity) = multiple queries combined into a formula."
|
msgid "Example: Usage = single query. (Requested) / (Capacity) = multiple queries combined into a formula."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,11 @@ gitlab:
|
||||||
limits:
|
limits:
|
||||||
cpu: 1.2G
|
cpu: 1.2G
|
||||||
memory: 2.8G
|
memory: 2.8G
|
||||||
|
deployment:
|
||||||
|
readinessProbe:
|
||||||
|
initialDelaySeconds: 5 # Default is 0
|
||||||
|
periodSeconds: 15 # Default is 10
|
||||||
|
timeoutSeconds: 5 # Default is 2
|
||||||
workhorse:
|
workhorse:
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
|
@ -75,6 +80,10 @@ gitlab:
|
||||||
limits:
|
limits:
|
||||||
cpu: 200m
|
cpu: 200m
|
||||||
memory: 200M
|
memory: 200M
|
||||||
|
readinessProbe:
|
||||||
|
initialDelaySeconds: 5 # Default is 0
|
||||||
|
periodSeconds: 15 # Default is 10
|
||||||
|
timeoutSeconds: 5 # Default is 2
|
||||||
gitlab-runner:
|
gitlab-runner:
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
|
|
|
@ -14,8 +14,6 @@ describe 'Issue Boards', :js do
|
||||||
let!(:bug) { create(:label, project: project, name: 'Bug') }
|
let!(:bug) { create(:label, project: project, name: 'Bug') }
|
||||||
let!(:regression) { create(:label, project: project, name: 'Regression') }
|
let!(:regression) { create(:label, project: project, name: 'Regression') }
|
||||||
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
|
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
|
||||||
let!(:scoped_label_1) { create(:label, project: project, name: 'Scoped::Label1') }
|
|
||||||
let!(:scoped_label_2) { create(:label, project: project, name: 'Scoped::Label2') }
|
|
||||||
let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) }
|
let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) }
|
||||||
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
|
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
|
||||||
let(:board) { create(:board, project: project) }
|
let(:board) { create(:board, project: project) }
|
||||||
|
@ -29,8 +27,6 @@ describe 'Issue Boards', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_licensed_features(scoped_labels: true)
|
|
||||||
|
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
@ -313,33 +309,6 @@ describe 'Issue Boards', :js do
|
||||||
expect(card).to have_content(bug.title)
|
expect(card).to have_content(bug.title)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes existing scoped label' do
|
|
||||||
click_card(card)
|
|
||||||
|
|
||||||
page.within('.labels') do
|
|
||||||
click_link 'Edit'
|
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
click_link scoped_label_1.title
|
|
||||||
click_link scoped_label_2.title
|
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
find('.dropdown-menu-close-icon').click
|
|
||||||
|
|
||||||
page.within('.value') do
|
|
||||||
expect(page).to have_selector('.badge', count: 3)
|
|
||||||
expect(page).not_to have_content(scoped_label_1.title)
|
|
||||||
expect(page).to have_content(scoped_label_2.title)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(card).to have_selector('.badge', count: 3)
|
|
||||||
expect(card).not_to have_content(scoped_label_1.title)
|
|
||||||
expect(card).to have_content(scoped_label_2.title)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds a multiple labels' do
|
it 'adds a multiple labels' do
|
||||||
click_card(card)
|
click_card(card)
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,43 @@ describe TodosFinder do
|
||||||
expect(todos).to match_array([todo1])
|
expect(todos).to match_array([todo1])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when filtering for actions' do
|
||||||
|
let!(:todo1) { create(:todo, user: user, project: project, target: issue, action: Todo::ASSIGNED) }
|
||||||
|
let!(:todo2) { create(:todo, user: user, group: group, target: merge_request, action: Todo::DIRECTLY_ADDRESSED) }
|
||||||
|
|
||||||
|
context 'by action ids' do
|
||||||
|
it 'returns the expected todos' do
|
||||||
|
todos = finder.new(user, { action_id: Todo::DIRECTLY_ADDRESSED }).execute
|
||||||
|
|
||||||
|
expect(todos).to match_array([todo2])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'multiple actions' do
|
||||||
|
it 'returns the expected todos' do
|
||||||
|
todos = finder.new(user, { action_id: [Todo::DIRECTLY_ADDRESSED, Todo::ASSIGNED] }).execute
|
||||||
|
|
||||||
|
expect(todos).to match_array([todo2, todo1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'by action names' do
|
||||||
|
it 'returns the expected todos' do
|
||||||
|
todos = finder.new(user, { action: :directly_addressed }).execute
|
||||||
|
|
||||||
|
expect(todos).to match_array([todo2])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'multiple actions' do
|
||||||
|
it 'returns the expected todos' do
|
||||||
|
todos = finder.new(user, { action: [:directly_addressed, :assigned] }).execute
|
||||||
|
|
||||||
|
expect(todos).to match_array([todo2, todo1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with subgroups' do
|
context 'with subgroups' do
|
||||||
let(:subgroup) { create(:group, parent: group) }
|
let(:subgroup) { create(:group, parent: group) }
|
||||||
let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) }
|
let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) }
|
||||||
|
|
Loading…
Reference in a new issue