Commit fe87d636 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'ce-com/master' into ce-to-ee

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents e6430495 b525aff6
......@@ -109,7 +109,7 @@ setup-test-env:
<<: *dedicated-runner
stage: prepare
script:
- bundle exec rake assets:precompile 2>/dev/null
- bundle exec rake gitlab:assets:compile 2>/dev/null
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
expire_in: 7d
......
......@@ -291,6 +291,7 @@ group :development, :test do
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
gem 'rspec_profiling'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'
......
......@@ -666,6 +666,11 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
rspec_profiling (0.0.4)
activerecord
pg
rails
sqlite3
rubocop (0.46.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
......@@ -767,6 +772,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.11)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
......@@ -998,6 +1004,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rspec_profiling
rubocop (~> 0.46.0)
rubocop-rspec (~> 1.9.1)
ruby-fogbugz (~> 0.2.1)
......
......@@ -86,7 +86,6 @@
var $sidebarGutterToggle = $('.js-sidebar-toggle');
var $flash = $('.flash-container');
var bootstrapBreakpoint = bp.getBreakpointSize();
var checkInitialSidebarSize;
var fitSidebarForSize;
// Set the default path for all cookies to GitLab's root directory
......@@ -251,19 +250,11 @@
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
checkInitialSidebarSize = function () {
bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint === 'xs' || 'sm') {
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
$window.off('resize.app').on('resize.app', function () {
return fitSidebarForSize();
});
gl.awardsHandler = new AwardsHandler();
checkInitialSidebarSize();
new Aside();
// bind sidebar events
new gl.Sidebar();
});
......
......@@ -3,7 +3,7 @@
(function() {
this.CommitsList = (function() {
function CommitsList() {}
var CommitsList = {};
CommitsList.timer = null;
......@@ -20,6 +20,7 @@
});
this.content = $("#commits-list");
this.searchField = $("#commits-search");
this.lastSearch = this.searchField.val();
return this.initSearch();
};
......@@ -37,6 +38,7 @@
var commitsUrl, form, search;
form = $(".commits-search-form");
search = CommitsList.searchField.val();
if (search === CommitsList.lastSearch) return;
commitsUrl = form.attr("action") + '?' + form.serialize();
CommitsList.content.fadeTo('fast', 0.5);
return $.ajax({
......@@ -47,12 +49,16 @@
return CommitsList.content.fadeTo('fast', 1.0);
},
success: function(data) {
CommitsList.lastSearch = search;
CommitsList.content.html(data.html);
return history.replaceState({
page: commitsUrl
// Change url so if user reload a page - search results are saved
}, document.title, commitsUrl);
},
error: function() {
CommitsList.lastSearch = null;
},
dataType: "json"
});
};
......
......@@ -28,7 +28,12 @@
if (lastToken !== searchToken) {
const title = updatedItem.title.toLowerCase();
let value = lastToken.value.toLowerCase();
value = value.replace(/"(.*?)"/g, str => str.slice(1).slice(0, -1));
// Removes the first character if it is a quotation so that we can search
// with multiple words
if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
value = value.slice(1);
}
// Eg. filterSymbol = ~ for labels
const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1;
......@@ -83,8 +88,9 @@
const selectionStart = input.selectionStart;
let inputValue = input.value;
// Replace all spaces inside quote marks with underscores
// (will continue to match entire string until an end quote is found if any)
// This helps with matching the beginning & end of a token:key
inputValue = inputValue.replace(/("(.*?)"|:\s+)/g, str => str.replace(/\s/g, '_'));
inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_'));
// Get the right position for the word selected
// Regex matches first space
......
......@@ -196,7 +196,8 @@
});
if (searchToken) {
paths.push(`search=${encodeURIComponent(searchToken)}`);
const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+');
paths.push(`search=${sanitized}`);
}
Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`);
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, max-len */
/* global UsersSelect */
/* global Cookies */
/* global bp */
(function() {
this.IssuableContext = (function() {
......@@ -37,6 +39,13 @@
}, 0);
}
});
window.addEventListener('beforeunload', function() {
// collapsed_gutter cookie hides the sidebar
var bpBreakpoint = bp.getBreakpointSize();
if (bpBreakpoint === 'xs' || bpBreakpoint === 'sm') {
Cookies.set('collapsed_gutter', true);
}
});
$(".right-sidebar").niceScroll();
}
......
/*= require ace-rails-ap */
/*= require ace/ace */
/*= require ace/ext-searchbox */
/*= require ./ace/ace_config_paths */
<%
ace_gem_path = Bundler.rubygems.find_name('ace-rails-ap').first.full_gem_path
ace_workers = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/worker-*.js'].sort.map do |file|
File.basename(file, '.js').sub(/^worker-/, '')
end
ace_modes = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/mode-*.js'].sort.map do |file|
File.basename(file, '.js').sub(/^mode-/, '')
end
%>
(function() {
window.gon = window.gon || {};
var basePath = (window.gon.relative_url_root || '').replace(/\/$/, '') + '/assets/ace';
ace.config.set('basePath', basePath);
// configure paths for all worker modules
<% ace_workers.each do |worker| %>
ace.config.setModuleUrl('ace/mode/<%= worker %>_worker', basePath + '/worker-<%= worker %>.js');
<% end %>
// configure paths for all mode modules
<% ace_modes.each do |mode| %>
ace.config.setModuleUrl('ace/mode/<%= mode %>', basePath + '/mode-<%= mode %>.js');
<% end %>
})();
......@@ -158,7 +158,7 @@
};
Calendar.prototype.renderMonths = function() {
return this.svg.append('g').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
return date.x;
}).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
return function(date) {
......
.calender-block {
padding-left: 0;
padding-right: 0;
direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
overflow-x: scroll;
......
......@@ -26,10 +26,6 @@
border: 0;
}
}
.container-fluid {
@extend .fixed-width-container;
}
}
}
......
......@@ -420,10 +420,6 @@
.merge-request-tabs-holder {
background-color: $white-light;
.container-limited {
max-width: $limited-layout-width;
}
&.affix {
top: 100px;
left: 0;
......@@ -433,11 +429,27 @@
@media (max-width: $screen-xs-max) {
right: 0;
}
.merge-request-tabs-container {
padding-left: $gl-padding;
padding-right: $gl-padding;
}
}
}
&:not(.affix) .container-fluid {
padding-left: 0;
padding-right: 0;
.limit-container-width {
.merge-request-tabs-container {
max-width: $limited-layout-width;
margin-left: auto;
margin-right: auto;
}
}
.limit-container-width:not(.container-limited) {
.merge-request-tabs-holder:not(.affix) {
.merge-request-tabs-container {
max-width: $limited-layout-width - ($gl-padding * 2);
}
}
}
......
......@@ -18,15 +18,14 @@ class AutocompleteController < ApplicationController
if params[:search].blank?
# Include current user if available to filter by "Me"
if params[:current_user].present? && current_user
@users = @users.where.not(id: current_user.id)
@users = [current_user, *@users]
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users = [author, *@users] if author
@users = [author, *@users].uniq if author
end
@users.uniq!
end
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
......
......@@ -171,7 +171,7 @@ class ApplicationSetting < ActiveRecord::Base
Rails.cache.delete(CACHE_KEY)
rescue
# Gracefully handle when Redis is not available. For example,
# omnibus may fail here during assets:precompile.
# omnibus may fail here during gitlab:assets:compile.
end
def self.cached
......
- status = local_assigns.fetch(:status)
- link = local_assigns.fetch(:link, true)
- css_classes = "ci-status ci-#{status.group}"
- if status.has_details?
- if link && status.has_details?
= link_to status.details_path, class: css_classes do
= custom_icon(status.icon)
= status.text
......
.content-block.build-header
.header-content
= render 'ci/status/badge', status: @build.detailed_status(current_user)
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
Build
%strong ##{@build.id}
%strong.js-build-id ##{@build.id}
in pipeline
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
......
......@@ -9,7 +9,7 @@
%fieldset.append-bottom-0
.row
.form-group.col-md-9
= f.label :name, class: 'label-light' do
= f.label :name, class: 'label-light', for: 'project_name_edit' do
Project name
= f.text_field :name, class: "form-control", id: "project_name_edit"
......
......@@ -19,7 +19,7 @@
= render 'shared/issuable/nav', type: :issues
.nav-controls
- if current_user
= link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10', title: 'Subscribe' do
= link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
= icon('rss')
- if can? current_user, :create_issue, @project
= link_to new_namespace_project_issue_path(@project.namespace,
......
......@@ -52,7 +52,7 @@
= render 'award_emoji/awards_block', awardable: @merge_request, inline: true
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
%div{ class: container_class }
.merge-request-tabs-container
%ul.merge-request-tabs.nav-links.no-top.no-bottom
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
......@@ -115,4 +115,3 @@
});
var mrRefreshWidgetUrl = "#{mr_widget_refresh_url(@merge_request)}";
......@@ -17,4 +17,4 @@
- disabled_title = @service.disabled_title
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service), class: "btn #{disabled_class}", title: disabled_title
= link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
= link_to "Cancel", namespace_project_settings_integrations_path(@project.namespace, @project), class: "btn btn-cancel"
---
title: Add link verification to badge partial in order to render a badge without a link
merge_request: 8740
author:
---
title: Fix filtering with multiple words
merge_request: 8830
author:
---
title: Fix project name label's for reference in project settings
merge_request: 8795
author:
---
title: contribution calendar scrolls from right to left
merge_request:
author:
---
title: Fixed services form cancel not redirecting back the integrations settings view
merge_request: 8843
author:
---
title: Fix search bar search param encoding
merge_request: 8753
author:
---
title: Fixed Issuable sidebar not closing on smaller/mobile sized screens
merge_request:
author:
---
title: Fixed merge requests tab extra margin when fixed to window
merge_request:
author:
---
title: allow relative url change without recompiling frontend assets
merge_request: 8831
author:
module RspecProfilingConnection
def establish_connection
::RspecProfiling::Collectors::PSQL::Result.establish_connection(ENV['RSPEC_PROFILING_POSTGRES_URL'])
end
end
if Rails.env.test?
RspecProfiling.configure do |config|
if ENV['RSPEC_PROFILING_POSTGRES_URL']
RspecProfiling::Collectors::PSQL.prepend(RspecProfilingConnection)
config.collector = RspecProfiling::Collectors::PSQL
end
end
end
......@@ -172,14 +172,14 @@ Omnibus packages.
```
cd /home/git/gitlab
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
```
For omnibus versions, the unoptimized assets (JavaScript, CSS) are frozen at
the release of upstream GitLab. The omnibus version includes optimized versions
of those assets. Unless you are modifying the JavaScript / CSS code on your
production machine after installing the package, there should be no reason to redo
rake assets:precompile on the production machine. If you suspect that assets
rake gitlab:assets:compile on the production machine. If you suspect that assets
have been corrupted, you should reinstall the omnibus package.
## Tracking Deployments
......
......@@ -245,7 +245,7 @@ Example response:
```json
[
{
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"new_path": "doc/update/5.4-to-6.0.md",
"old_path": "doc/update/5.4-to-6.0.md",
"a_mode": null,
......
......@@ -211,6 +211,41 @@ suite first. See the
[StackProf documentation](https://github.com/tmm1/stackprof/blob/master/README.md)
for details.
## RSpec profiling
GitLab's development environment also includes the
[rspec_profiling](https://github.com/foraker/rspec_profiling) gem, which is used
to collect data on spec execution times. This is useful for analyzing the
performance of the test suite itself, or seeing how the performance of a spec
may have changed over time.
To activate profiling in your local environment, run the following:
```
$ export RSPEC_PROFILING=yes
$ rake rspec_profiling:install
```
This creates an SQLite3 database in `tmp/rspec_profiling`, into which statistics
are saved every time you run specs with the `RSPEC_PROFILING` environment
variable set.
Ad-hoc investigation of the collected results can be performed in an interactive
shell:
```
$ rake rspec_profiling:console
irb(main):001:0> results.count
=> 231
irb(main):002:0> results.last.attributes.keys
=> ["id", "commit", "date", "file", "line_number", "description", "time", "status", "exception", "query_count", "query_time", "request_count", "request_time", "created_at", "updated_at"]
irb(main):003:0> results.where(status: "passed").average(:time).to_s
=> "0.211340155844156"
```
These results can also be placed into a PostgreSQL database by setting the
`RSPEC_PROFILING_POSTGRES_URL` variable. This is used to profile the test suite
when running in the CI environment.
## Importance of Changes
When working on performance improvements, it's important to always ask yourself
......
......@@ -109,7 +109,7 @@ Dropdowns are used to allow users to choose one (or many) options from a list of
### Max size
The max height for dropdowns should target **10-15 items**. If the height of the dropdown is too large, the list becomes very hard to parse and it is easy to visually lose track of the item you are looking for. Usability also suffers as more mouse movement is required, and you have a larger area in which you hijack the scroll away from the page level. While it may initially seem counterintuitive to not show as many items as you can, it is actually quicker and easier to process the information when it is cropped at a reasonable height.
The max height for dropdowns should target **10-15** single line items, or **7-10** multi-line items. If the height of the dropdown is too large, the list becomes very hard to parse and it is easy to visually lose track of the item you are looking for. Usability also suffers as more mouse movement is required, and you have a larger area in which you hijack the scroll away from the page level. While it may initially seem counterintuitive to not show as many items as you can, it is actually quicker and easier to process the information when it is cropped at a reasonable height.
---
......
......@@ -102,6 +102,12 @@ When using the <kbd>Alt</kbd> keystrokes in Windows, use the numeric keypad, not
## Terminology
Only use the terms in the tables below.
### Projects and Groups
| Term | Use | :no_entry_sign: Don't |
| ---- | --- | ----- |
| Members | When discussing the people who are a part of a project or a group. | Don't use `users`. |
### Issues
#### Adjectives (states)
......@@ -117,7 +123,7 @@ Use `5 open issues` and don’t use `5 pending issues`.
#### Verbs (actions)
| Term | Use | Don’t |
| Term | Use | :no_entry_sign: Don’t |
| ---- | --- | --- |
| Add | Add an issue | Don’t use `create` or `new` |
| View | View an open or closed issue ||
......@@ -158,7 +164,7 @@ The form should be titled `Edit issue`. The submit button should be labeled `Sav
#### Verbs (actions)
| Term | Use | Don’t |
| Term | Use | :no_entry_sign: Don’t |
| ---- | --- | --- |
| Add | Add a merge request | Do not use `create` or `new` |
| View | View an open or merged merge request ||
......
......@@ -451,7 +451,7 @@ Check if GitLab and its environment are configured correctly:
### Compile Assets
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
### Start Your GitLab Instance
......
......@@ -113,14 +113,6 @@ Make sure to follow all steps below:
If you are using a custom init script, make sure to edit the above
gitlab-workhorse setting as needed.
1. After all the above changes recompile the assets. This is an important task
and will take some time to complete depending on the server resources:
```
cd /home/git/gitlab
sudo -u git -H bundle exec rake assets:clean assets:precompile RAILS_ENV=production
```
1. [Restart GitLab][] for the changes to take effect.
### Disable relative URL in GitLab
......
......@@ -57,7 +57,7 @@ sudo -u git -H bundle clean
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
```
### 4. Update gitlab-workhorse to the corresponding version
......
......@@ -61,7 +61,7 @@ module Gitlab
"Switch to new version" => %W(#{Gitlab.config.git.bin_path} checkout v#{latest_version}-ee),
"Install gems" => %W(bundle),
"Migrate DB" => %W(bundle exec rake db:migrate),
"Recompile assets" => %W(bundle exec rake assets:clean assets:precompile),
"Recompile assets" => %W(bundle exec rake gitlab:assets:clean gitlab:assets:compile),
"Clear cache" => %W(bundle exec rake cache:clear)
}
end
......
......@@ -31,8 +31,8 @@ echo 'Deploy: Bundle and migrate'
sudo -u git -H bundle --without aws development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
# return stashed changes (if necessary)
......
namespace :gitlab do
namespace :assets do
desc 'GitLab | Assets | Compile all frontend assets'
task :compile do
Rake::Task['assets:precompile'].invoke
Rake::Task['gitlab:assets:fix_urls'].invoke
end
desc 'GitLab | Assets | Clean up old compiled frontend assets'
task :clean do
Rake::Task['assets:clean'].invoke
end
desc 'GitLab | Assets | Remove all compiled frontend assets'
task :purge do
Rake::Task['assets:clobber'].invoke
end
desc 'GitLab | Assets | Fix all absolute url references in CSS'
task :fix_urls do
css_files = Dir['public/assets/*.css']
css_files.each do | file |
# replace url(/assets/*) with url(./*)
puts "Fixing #{file}"
system "sed", "-i", "-e", 's/url(\([\"\']\?\)\/assets\//url(\1.\//g', file
# rewrite the corresponding gzip file (if it exists)
gzip = "#{file}.gz"
if File.exist?(gzip)
puts "Fixing #{gzip}"
FileUtils.rm(gzip)
mtime = File.stat(file).mtime
File.open(gzip, 'wb+') do |f|
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
gz.mtime = mtime
gz.write IO.binread(file)
gz.close
File.utime(mtime, mtime, f.path)
end
end
end
end
end
end
require 'spec_helper'
describe Explore::ProjectsController do
let(:user) { create(:user) }
let(:visibility) { :public }
describe 'GET #trending' do
let!(:project_1) { create(:project, visibility, ci_id: 1) }
let!(:project_2) { create(:project, visibility, ci_id: 2) }
let!(:trending_project_1) { create(:trending_project, project: project_1) }
let!(:trending_project_2) { create(:trending_project, project: project_2) }
context 'sorting by update date' do
let(:project1) { create(:empty_project, :public, updated_at: 3.days.ago) }
let(:project2) { create(:empty_project, :public, updated_at: 1.day.ago) }
before do
sign_in(user)
end
before do
create(:trending_project, project: project1)
create(:trending_project, project: project2)
end
context 'sorting by update date' do
it 'sorts by last updated' do
get :trending, sort: 'updated_desc'
expect(assigns(:projects)).to eq [project_2, project_1]
expect(assigns(:projects)).to eq [project2, project1]
end
it 'sorts by oldest updated' do
get :trending, sort: 'updated_asc'
expect(assigns(:projects)).to eq [project_1, project_2]
expect(assigns(:projects)).to eq [project1, project2]
end
end
end
......
......@@ -2,6 +2,7 @@ require 'rails_helper'
feature 'Issue Sidebar', feature: true do
include WaitForAjax
include MobileHelpers
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
......@@ -59,6 +60,23 @@ feature 'Issue Sidebar', feature: true do
end
end
context 'sidebar', js: true do
it 'changes size when the screen size is smaller' do
sidebar_selector = 'aside.right-sidebar.right-sidebar-collapsed'
# Resize the window
resize_screen_sm
# Make sure the sidebar is collapsed
expect(page).to have_css(sidebar_selector)
# Once is collapsed let's open the sidebard and reload
open_issue_sidebar
refresh
expect(page).to have_css(sidebar_selector)
# Restore the window size as it was including the sidebar
restore_window_size
open_issue_sidebar
end
end
context 'creating a new label', js: true do
it 'shows option to crate a new label is present' do
page.within('.block.labels') do
......@@ -138,4 +156,11 @@ feature 'Issue Sidebar', feature: true do
def visit_issue(project, issue)
visit namespace_project_issue_path(project.namespace, project, issue)
end
def open_issue_sidebar
page.within('aside.right-sidebar.right-sidebar-collapsed') do
find('.js-sidebar-toggle').click
sleep 1
end
end
end
......@@ -37,7 +37,7 @@ describe 'Edit Project Settings', feature: true do
it 'shows errors for invalid project path/name' do
visit edit_namespace_project_path(project.namespace, project)
fill_in 'Project name', with: 'foo&bar'
fill_in 'project_name', with: 'foo&bar'
fill_in 'Path', with: 'foo&bar'
click_button 'Rename project'
......@@ -53,7 +53,7 @@ describe 'Edit Project Settings', feature: true do
it 'shows error for invalid project name' do
visit edit_namespace_project_path(project.namespace, project)
fill_in 'Project name', with: '🚀 foo bar ☁️'
fill_in 'project_name', with: '🚀 foo bar ☁️'
click_button 'Rename project'
......
/* global CommitsList */
//= require jquery.endless-scroll
//= require pager
//= require commits
(() => {
describe('Commits List', () => {
beforeEach(() => {
setFixtures(`
<form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master">
<input id="commits-search">
</form>
<ol id="commits-list"></ol>
`);
});
it('should be defined', () => {
expect(CommitsList).toBeDefined();
});
describe('on entering input', () => {
let ajaxSpy;
beforeEach(() => {
CommitsList.init(25);
CommitsList.searchField.val('');
spyOn(history, 'replaceState').and.stub();
ajaxSpy = spyOn(jQuery, 'ajax').and.callFake((req) => {
req.success({
data: '<li>Result</li>',
});
});
});
it('should save the last search string', () => {
CommitsList.searchField.val('GitLab');
CommitsList.filterResults();
expect(ajaxSpy).toHaveBeenCalled();
expect(CommitsList.lastSearch).toEqual('GitLab');
});
it('should not make ajax call if the input does not change', () => {
CommitsList.filterResults();
expect(ajaxSpy).not.toHaveBeenCalled();
expect(CommitsList.lastSearch).toEqual('');
});
});
});
})();
......@@ -64,6 +64,68 @@
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
expect(updatedItem.droplab_hidden).toBe(false);
});
describe('filters multiple word title', () => {
const multipleWordItem = {
title: 'Community Contributions',
};
it('should filter with double quote', () => {
input.value = 'label:"';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote and symbol', () => {
input.value = 'label:~"';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote and multiple words', () => {
input.value = 'label:"community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote, symbol and multiple words', () => {
input.value = 'label:~"community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote', () => {
input.value = 'label:\'';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and symbol', () => {
input.value = 'label:~\'';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and multiple words', () => {
input.value = 'label:\'community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote, symbol and multiple words', () => {
input.value = 'label:~\'community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
});
});
describe('filterHint', () => {
......@@ -130,5 +192,99 @@
expect(result).toBe(false);
});
});
describe('getInputSelectionPosition', () => {
describe('word with trailing spaces', () => {
const value = 'label:none ';
it('should return selectionStart when cursor is at the trailing space', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 11,
value,
});
expect(left).toBe(11);
expect(right).toBe(11);
});
it('should return input when cursor is at the start of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 0,
value,
});
expect(left).toBe(0);
expect(right).toBe(10);
});
it('should return input when cursor is at the middle of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 7,
value,
});
expect(left).toBe(0);
expect(right).toBe(10);
});
it('should return input when cursor is at the end of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 10,
value,
});
expect(left).toBe(0);
expect(right).toBe(10);
});
});
describe('multiple words', () => {
const value = 'label:~"Community Contribution"';
it('should return input when cursor is after the first word', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 17,
value,
});
expect(left).toBe(0);
expect(right).toBe(31);
});
it('should return input when cursor is before the second word', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 18,
value,
});
expect(left).toBe(0);
expect(right).toBe(31);
});
});
describe('incomplete multiple words', () => {
const value = 'label:~"Community Contribution';
it('should return entire input when cursor is at the start of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 0,
value,
});
expect(left).toBe(0);
expect(right).toBe(30);
});
it('should return entire input when cursor is at the end of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 30,
value,
});
expect(left).toBe(0);
expect(right).toBe(30);
});
});
});
});
})();
/* global Turbolinks */
//= require turbolinks
//= require lib/utils/common_utils
//= require filtered_search/filtered_search_token_keys
//= require filtered_search/filtered_search_tokenizer
//= require filtered_search/filtered_search_dropdown_manager
//= require filtered_search/filtered_search_manager
(() => {
describe('Filtered Search Manager', () => {
describe('search', () => {
let manager;
const defaultParams = '?scope=all&utf8=✓&state=opened';
function getInput() {
return document.querySelector('.filtered-search');
}
beforeEach(() => {
setFixtures(`
<input type='text' class='filtered-search' />
`);
spyOn(gl.FilteredSearchManager.prototype, 'bindEvents').and.callFake(() => {});
spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
manager = new gl.FilteredSearchManager();
});
afterEach(() => {
getInput().outerHTML = '';
});
it('should search with a single word', () => {
getInput().value = 'searchTerm';
spyOn(Turbolinks, 'visit').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
});
manager.search();
});
it('should search with multiple words', () => {
getInput().value = 'awesome search terms';
spyOn(Turbolinks, 'visit').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
});
manager.search();
});
it('should search with special characters', () => {
getInput().value = '~!@#$%^&*()_+{}:<>,.?/';
spyOn(Turbolinks, 'visit').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
});
manager.search();
});
});
});
})();
......@@ -9,6 +9,10 @@ require 'rspec/rails'
require 'shoulda/matchers'
require 'rspec/retry'
if ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING']
require 'rspec_profiling/rspec'
end
if ENV['CI'] && !ENV['NO_KNAPSACK']
require 'knapsack'
Knapsack::Adapters::RSpecAdapter.bind
......
module MobileHelpers
def resize_screen_sm
resize_window(900, 768)
end
def restore_window_size
resize_window(1366, 768)
end
def resize_window(width, height)
page.driver.resize_window width, height
end
end
......@@ -15,6 +15,36 @@ describe 'projects/builds/show', :view do
allow(view).to receive(:can?).and_return(true)
end
describe 'build information in header' do
let(:build) do
create(:ci_build, :success, environment: 'staging')
end
before do
render
end
it 'shows status name' do
expect(rendered).to have_css('.ci-status.ci-success', text: 'passed')
end
it 'does not render a link to the build' do
expect(rendered).not_to have_link('passed')
end
it 'shows build id' do
expect(rendered).to have_css('.js-build-id', text: build.id)
end
it 'shows a link to the pipeline' do
expect(rendered).to have_link(build.pipeline.id)
end
it 'shows a link to the commit' do
expect(rendered).to have_link(build.pipeline.short_sha)
end
end
describe 'environment info in build view' do
context 'build with latest deployment' do
let(:build) do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment