diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b261bd5938..83e41a11e52 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -296,9 +296,9 @@ might be edited to make them small and simple. Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker. -For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should -be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may -need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself. +For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should +be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may +need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself. If you want to create something yourself, consider opening an issue first to discuss whether it is interesting to include this in GitLab. @@ -658,7 +658,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [license-finder-doc]: doc/development/licensing.md [GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues [polling-etag]: https://docs.gitlab.com/ce/development/polling.html -[testing]: doc/development/testing.md +[testing]: doc/development/testing_guide/index.md [^1]: Please note that specs other than JavaScript specs are considered backend code. diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 8298bb08b2d..301092317fe 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.43.0 +0.46.0 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 5508e17c23b..c5b7013b9c5 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1,2 +1 @@ -5.9.3 - +5.9.4 diff --git a/Gemfile b/Gemfile index e43370b1ab6..0dca265d1c7 100644 --- a/Gemfile +++ b/Gemfile @@ -398,7 +398,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.39.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.41.0', require: 'gitaly' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 0b56058defc..5bf1d6a8bc2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -274,7 +274,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.39.0) + gitaly-proto (0.41.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -1023,7 +1023,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly-proto (~> 0.39.0) + gitaly-proto (~> 0.41.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) diff --git a/PROCESS.md b/PROCESS.md index 5e65bb59246..06963243b25 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -1,4 +1,4 @@ -## GitLab Core Team & GitLab Inc. Contribution Process +## GitLab core team & GitLab Inc. contribution process --- diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 4f01345ee3b..622764107ad 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,8 +1,8 @@ /* eslint-disable class-methods-use-this */ -/* global Flash */ import _ from 'underscore'; import Cookies from 'js-cookie'; import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils'; +import Flash from './flash'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'; diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js index 8641a6fdae6..062577af385 100644 --- a/app/assets/javascripts/blob/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq_viewer.js @@ -1,9 +1,8 @@ -/* global Flash */ - +import Flash from '../flash'; import BalsamiqViewer from './balsamiq/balsamiq_viewer'; function onError() { - const flash = new window.Flash('Balsamiq file could not be loaded.'); + const flash = new Flash('Balsamiq file could not be loaded.'); return flash; } diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js index a20c6ca7a21..583e5faa506 100644 --- a/app/assets/javascripts/blob/file_template_mediator.js +++ b/app/assets/javascripts/blob/file_template_mediator.js @@ -1,6 +1,5 @@ /* eslint-disable class-methods-use-this */ -/* global Flash */ - +import Flash from '../flash'; import FileTemplateTypeSelector from './template_selectors/type_selector'; import BlobCiYamlSelector from './template_selectors/ci_yaml_selector'; import DockerfileSelector from './template_selectors/dockerfile_selector'; diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js index e0b73f13d36..54132e8537b 100644 --- a/app/assets/javascripts/blob/viewer/index.js +++ b/app/assets/javascripts/blob/viewer/index.js @@ -1,4 +1,4 @@ -/* global Flash */ +import Flash from '../../flash'; import { handleLocationHash } from '../../lib/utils/common_utils'; export default class BlobViewer { diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index 815248f38ee..ef4093b59e3 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -1,10 +1,10 @@ /* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */ /* global BoardService */ -/* global Flash */ import _ from 'underscore'; import Vue from 'vue'; import VueResource from 'vue-resource'; +import Flash from '../flash'; import FilteredSearchBoards from './filtered_search_boards'; import eventHub from './eventhub'; import './models/issue'; diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js index 590b7be36e3..7f3afefc9cc 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js +++ b/app/assets/javascripts/boards/components/board_sidebar.js @@ -3,9 +3,9 @@ /* global MilestoneSelect */ /* global LabelsSelect */ /* global Sidebar */ -/* global Flash */ import Vue from 'vue'; +import Flash from '../../flash'; import eventHub from '../../sidebar/event_hub'; import AssigneeTitle from '../../sidebar/components/assignees/assignee_title'; import Assignees from '../../sidebar/components/assignees/assignees'; diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js index a656f0546c0..de9e44cef35 100644 --- a/app/assets/javascripts/boards/components/modal/footer.js +++ b/app/assets/javascripts/boards/components/modal/footer.js @@ -1,7 +1,7 @@ /* eslint-disable no-new */ -/* global Flash */ import Vue from 'vue'; +import Flash from '../../../flash'; import './lists_dropdown'; const ModalStore = gl.issueBoards.ModalStore; diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js index 1e623cf58b7..1ad97211934 100644 --- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js +++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js @@ -1,7 +1,7 @@ /* eslint-disable no-new */ -/* global Flash */ import Vue from 'vue'; +import Flash from '../../../flash'; const Store = gl.issueBoards.BoardsStore; diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js index 19388f1f9ae..ace89398943 100644 --- a/app/assets/javascripts/build_artifacts.js +++ b/app/assets/javascripts/build_artifacts.js @@ -1,30 +1,30 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */ +/* eslint-disable func-names, prefer-arrow-callback, no-return-assign */ import { visitUrl } from './lib/utils/url_utility'; import { convertPermissionToBoolean } from './lib/utils/common_utils'; -window.BuildArtifacts = (function() { - function BuildArtifacts() { +export default class BuildArtifacts { + constructor() { this.disablePropagation(); this.setupEntryClick(); this.setupTooltips(); } - - BuildArtifacts.prototype.disablePropagation = function() { - $('.top-block').on('click', '.download', function(e) { + // eslint-disable-next-line class-methods-use-this + disablePropagation() { + $('.top-block').on('click', '.download', function (e) { return e.stopPropagation(); }); - return $('.tree-holder').on('click', 'tr[data-link] a', function(e) { + return $('.tree-holder').on('click', 'tr[data-link] a', function (e) { return e.stopImmediatePropagation(); }); - }; - - BuildArtifacts.prototype.setupEntryClick = function() { - return $('.tree-holder').on('click', 'tr[data-link]', function(e) { + } + // eslint-disable-next-line class-methods-use-this + setupEntryClick() { + return $('.tree-holder').on('click', 'tr[data-link]', function () { visitUrl(this.dataset.link, convertPermissionToBoolean(this.dataset.externalLink)); }); - }; - - BuildArtifacts.prototype.setupTooltips = function() { + } + // eslint-disable-next-line class-methods-use-this + setupTooltips() { $('.js-artifact-tree-tooltip').tooltip({ placement: 'bottom', // Stop the tooltip from hiding when we stop hovering the element directly @@ -41,7 +41,5 @@ window.BuildArtifacts = (function() { .on('mouseleave', (e) => { $(e.currentTarget).find('.js-artifact-tree-tooltip').tooltip('hide'); }); - }; - - return BuildArtifacts; -})(); + } +} diff --git a/app/assets/javascripts/build_variables.js b/app/assets/javascripts/build_variables.js index c955a9ac2ea..35edf3e0017 100644 --- a/app/assets/javascripts/build_variables.js +++ b/app/assets/javascripts/build_variables.js @@ -1,8 +1,10 @@ -/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren */ +/* eslint-disable func-names*/ -$(function() { - $('.reveal-variables').off('click').on('click', function() { - $('.js-build-variables').toggle(); - $(this).hide(); - }); -}); +export default function handleRevealVariables() { + $('.js-reveal-variables') + .off('click') + .on('click', function () { + $('.js-build-variables').toggle(); + $(this).hide(); + }); +} diff --git a/app/assets/javascripts/ci_lint_editor.js b/app/assets/javascripts/ci_lint_editor.js index dd4a08a2f31..b9469e5b7cb 100644 --- a/app/assets/javascripts/ci_lint_editor.js +++ b/app/assets/javascripts/ci_lint_editor.js @@ -1,7 +1,4 @@ - -window.gl = window.gl || {}; - -class CILintEditor { +export default class CILintEditor { constructor() { this.editor = window.ace.edit('ci-editor'); this.textarea = document.querySelector('#content'); @@ -13,5 +10,3 @@ class CILintEditor { }); } } - -gl.CILintEditor = CILintEditor; diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js index ff2f2c81971..bf40eb3ee11 100644 --- a/app/assets/javascripts/create_merge_request_dropdown.js +++ b/app/assets/javascripts/create_merge_request_dropdown.js @@ -1,5 +1,5 @@ /* eslint-disable no-new */ -/* global Flash */ +import Flash from './flash'; import DropLab from './droplab/drop_lab'; import ISetter from './droplab/plugins/input_setter'; diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index cdf5e3c0290..49bb6c52180 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -1,7 +1,6 @@ -/* global Flash */ - import Vue from 'vue'; import Cookies from 'js-cookie'; +import Flash from '../flash'; import Translate from '../vue_shared/translate'; import banner from './components/banner.vue'; import stageCodeComponent from './components/stage_code_component.vue'; diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index a663e30dfd0..54e13b79a4f 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -1,5 +1,5 @@ " } it { is_expected.to render_template('new') } - it { project.repository.branch_names.include?('feature/test') } + it { project.repository.branch_exists?('feature/test') } end end diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 2c732aaf4ed..7c4a22c94c2 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -73,6 +73,12 @@ FactoryGirl.define do merge_user author end + trait :remove_source_branch do + merge_params do + { 'force_remove_source_branch' => '1' } + end + end + after(:build) do |merge_request| target_project = merge_request.target_project source_project = merge_request.source_project diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb index 4c120b09345..1cb78410957 100644 --- a/spec/features/dashboard/groups_list_spec.rb +++ b/spec/features/dashboard/groups_list_spec.rb @@ -1,14 +1,15 @@ require 'spec_helper' feature 'Dashboard Groups page', :js do - let!(:user) { create :user } - let!(:group) { create(:group) } - let!(:nested_group) { create(:group, :nested) } - let!(:another_group) { create(:group) } + let(:user) { create :user } + let(:group) { create(:group) } + let(:nested_group) { create(:group, :nested) } + let(:another_group) { create(:group) } it 'shows groups user is member of' do group.add_owner(user) nested_group.add_owner(user) + expect(another_group).to be_persisted sign_in(user) visit dashboard_groups_path @@ -22,6 +23,7 @@ feature 'Dashboard Groups page', :js do before do group.add_owner(user) nested_group.add_owner(user) + expect(another_group).to be_persisted sign_in(user) @@ -51,7 +53,7 @@ feature 'Dashboard Groups page', :js do end end - describe 'group with subgroups' do + describe 'group with subgroups', :nested_groups do let!(:subgroup) { create(:group, :public, parent: group) } before do @@ -90,7 +92,8 @@ feature 'Dashboard Groups page', :js do end describe 'when using pagination' do - let(:group2) { create(:group) } + let(:group) { create(:group, created_at: 5.days.ago) } + let(:group2) { create(:group, created_at: 2.days.ago) } before do group.add_owner(user) @@ -102,12 +105,9 @@ feature 'Dashboard Groups page', :js do visit dashboard_groups_path end - it 'shows pagination' do - expect(page).to have_selector('.gl-pagination') - expect(page).to have_selector('.gl-pagination .page', count: 2) - end - it 'loads results for next page' do + expect(page).to have_selector('.gl-pagination .page', count: 2) + # Check first page expect(page).to have_content(group2.full_name) expect(page).to have_selector("#group-#{group2.id}") diff --git a/spec/features/explore/user_explores_projects_spec.rb b/spec/features/explore/user_explores_projects_spec.rb new file mode 100644 index 00000000000..6ac9497b024 --- /dev/null +++ b/spec/features/explore/user_explores_projects_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe 'User explores projects' do + set(:archived_project) { create(:project, :archived) } + set(:internal_project) { create(:project, :internal) } + set(:private_project) { create(:project, :private) } + set(:public_project) { create(:project, :public) } + + shared_examples_for 'shows public projects' do + it 'shows projects' do + expect(page).to have_content(public_project.title) + expect(page).not_to have_content(internal_project.title) + expect(page).not_to have_content(private_project.title) + expect(page).not_to have_content(archived_project.title) + end + end + + shared_examples_for 'shows public and internal projects' do + it 'shows projects' do + expect(page).to have_content(public_project.title) + expect(page).to have_content(internal_project.title) + expect(page).not_to have_content(private_project.title) + expect(page).not_to have_content(archived_project.title) + end + end + + context 'when not signed in' do + context 'when viewing public projects' do + before do + visit(explore_projects_path) + end + + include_examples 'shows public projects' + end + end + + context 'when signed in' do + set(:user) { create(:user) } + + before do + sign_in(user) + end + + context 'when viewing public projects' do + before do + visit(explore_projects_path) + end + + include_examples 'shows public and internal projects' + end + + context 'when viewing most starred projects' do + before do + visit(starred_explore_projects_path) + end + + include_examples 'shows public and internal projects' + end + + context 'when viewing trending projects' do + before do + [archived_project, public_project].each { |project| create(:note_on_issue, project: project) } + + TrendingProject.refresh! + + visit(trending_explore_projects_path) + end + + include_examples 'shows public projects' + end + end +end diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 56144d17d4f..12aa54a3da1 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -18,6 +18,27 @@ feature 'Group milestones', :js do visit new_group_milestone_path(group) end + it 'renders description preview' do + form = find('.gfm-form') + + form.fill_in(:milestone_description, with: '') + + click_link('Preview') + + preview = find('.js-md-preview') + + expect(preview).to have_content('Nothing to preview.') + + click_link('Write') + + form.fill_in(:milestone_description, with: ':+1: Nice') + + click_link('Preview') + + expect(preview).to have_css('gl-emoji') + expect(find('#milestone_description', visible: false)).not_to be_visible + end + it 'creates milestone with start date' do fill_in 'Title', with: 'testing' find('#milestone_start_date').click diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb index b475bcb3921..2b580ca1809 100644 --- a/spec/features/merge_requests/diff_notes_resolve_spec.rb +++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb @@ -88,14 +88,24 @@ feature 'Diff notes resolve', :js do end end - it 'hides resolved discussion' do - page.within '.diff-content' do - click_button 'Resolve discussion' + describe 'resolved discussion' do + before do + page.within '.diff-content' do + click_button 'Resolve discussion' + end + + visit_merge_request end - visit_merge_request + it 'hides when resolve discussion is clicked' do + expect(page).to have_selector('.discussion-body', visible: false) + end - expect(page).to have_selector('.discussion-body', visible: false) + it 'shows resolved discussion when toggled' do + find(".timeline-content .discussion[data-discussion-id='#{note.discussion_id}'] .discussion-toggle-button").click + + expect(page.find(".timeline-content #note_#{note.noteable_id}")).to be_visible + end end it 'allows user to resolve from reply form without a comment' do diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb index e32bbaf517b..21bbf82be77 100644 --- a/spec/features/merge_requests/diffs_spec.rb +++ b/spec/features/merge_requests/diffs_spec.rb @@ -44,12 +44,8 @@ feature 'Diffs URL', :js do visit "#{diffs_project_merge_request_path(project, merge_request)}#{fragment}" end - it 'shows collapsed note' do - wait_for_requests - - expect(page).to have_selector('.discussion-notes.collapsed') do |note_container| - expect(note_container).to have_selector(fragment, visible: false) - end + it 'shows expanded note' do + expect(page).to have_selector(fragment, visible: true) end end end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index c928459f911..026aa03f7cf 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -27,6 +27,7 @@ feature 'Import/Export - project import integration test', :js do select2(namespace.id, from: '#project_namespace_id') fill_in :project_path, with: project_path, visible: true + click_import_project_tab click_link 'GitLab export' expect(page).to have_content('Import an exported GitLab project') @@ -51,6 +52,7 @@ feature 'Import/Export - project import integration test', :js do context 'path is not prefilled' do scenario 'user imports an exported project successfully' do visit new_project_path + click_import_project_tab click_link 'GitLab export' fill_in :path, with: 'test-project-path', visible: true @@ -72,6 +74,7 @@ feature 'Import/Export - project import integration test', :js do select2(user.namespace.id, from: '#project_namespace_id') fill_in :project_path, with: project.name, visible: true + click_import_project_tab click_link 'GitLab export' attach_file('file', file) click_on 'Import project' @@ -81,19 +84,6 @@ feature 'Import/Export - project import integration test', :js do end end - context 'when limited to the default user namespace' do - scenario 'passes correct namespace ID in the URL' do - visit new_project_path - - fill_in :project_path, with: 'test-project-path', visible: true - - click_link 'GitLab export' - - expect(page).to have_content('GitLab project export') - expect(URI.parse(current_url).query).to eq("namespace_id=#{user.namespace.id}&path=test-project-path") - end - end - def wiki_exists?(project) wiki = ProjectWiki.new(project) File.exist?(wiki.repository.path_to_repo) && !wiki.repository.empty? @@ -102,4 +92,8 @@ feature 'Import/Export - project import integration test', :js do def project_hook_exists?(project) Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists? end + + def click_import_project_tab + find('#import-project-tab').trigger('click') + end end diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index 62b23121c5a..9f67216705d 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -118,7 +118,7 @@ feature 'issuable templates', :js do context 'user creates a merge request from a forked project using templates' do let(:template_content) { 'this is a test "feature-proposal" template' } let(:fork_user) { create(:user) } - let(:forked_project) { fork_project(project, fork_user) } + let(:forked_project) { fork_project(project, fork_user, repository: true) } let(:merge_request) { create(:merge_request, :with_diffs, source_project: forked_project, target_project: project) } background do diff --git a/spec/features/projects/issues/list_spec.rb b/spec/features/projects/issues/list_spec.rb deleted file mode 100644 index 9fc03f49f5b..00000000000 --- a/spec/features/projects/issues/list_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -feature 'Issues List' do - let(:user) { create(:user) } - let(:project) { create(:project) } - - background do - project.team << [user, :developer] - - sign_in(user) - end - - scenario 'user does not see create new list button' do - create(:issue, project: project) - - visit project_issues_path(project) - - expect(page).not_to have_selector('.js-new-board-list') - end -end diff --git a/spec/features/projects/issues/user_views_issues_spec.rb b/spec/features/projects/issues/user_views_issues_spec.rb new file mode 100644 index 00000000000..d35009b8974 --- /dev/null +++ b/spec/features/projects/issues/user_views_issues_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe 'User views issues' do + set(:user) { create(:user) } + + shared_examples_for 'shows issues' do + it 'shows issues' do + expect(page).to have_content(project.name) + .and have_content(issue1.title) + .and have_content(issue2.title) + .and have_no_selector('.js-new-board-list') + end + end + + context 'when project is public' do + set(:project) { create(:project_empty_repo, :public) } + set(:issue1) { create(:issue, project: project) } + set(:issue2) { create(:issue, project: project) } + + context 'when signed in' do + before do + project.add_developer(user) + sign_in(user) + + visit(project_issues_path(project)) + end + + include_examples 'shows issues' + end + + context 'when not signed in' do + before do + visit(project_issues_path(project)) + end + + include_examples 'shows issues' + end + end + + context 'when project is internal' do + set(:project) { create(:project_empty_repo, :internal) } + set(:issue1) { create(:issue, project: project) } + set(:issue2) { create(:issue, project: project) } + + context 'when signed in' do + before do + project.add_developer(user) + sign_in(user) + + visit(project_issues_path(project)) + end + + include_examples 'shows issues' + end + end +end diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index d265191e0c6..171b001aa7a 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -300,13 +300,13 @@ feature 'Jobs' do shared_examples 'expected variables behavior' do it 'shows variable key and value after click', :js do - expect(page).to have_css('.reveal-variables') + expect(page).to have_css('.js-reveal-variables') expect(page).not_to have_css('.js-build-variable') expect(page).not_to have_css('.js-build-value') click_button 'Reveal Variables' - expect(page).not_to have_css('.reveal-variables') + expect(page).not_to have_css('.js-reveal-variables') expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1') end diff --git a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb index 8970cf54457..3aac93eaf7c 100644 --- a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb +++ b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb @@ -6,7 +6,7 @@ describe 'User views an open merge request' do end context 'when a merge request does not have repository' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } before do visit(merge_request_path(merge_request)) diff --git a/spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb b/spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb index 07b8c1ef479..bf95dbb7d09 100644 --- a/spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb +++ b/spec/features/projects/merge_requests/user_views_open_merge_requests_spec.rb @@ -1,72 +1,115 @@ require 'spec_helper' describe 'User views open merge requests' do - let(:project) { create(:project, :public, :repository) } + set(:user) { create(:user) } - context "when the target branch is the project's default branch" do - let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let!(:closed_merge_request) { create(:closed_merge_request, source_project: project, target_project: project) } - - before do - visit(project_merge_requests_path(project)) - end - - it 'shows open merge requests' do - expect(page).to have_content(merge_request.title).and have_no_content(closed_merge_request.title) - end - - it 'does not show target branch name' do - expect(page).to have_content(merge_request.title) - expect(find('.issuable-info')).not_to have_content(project.default_branch) + shared_examples_for 'shows merge requests' do + it 'shows merge requests' do + expect(page).to have_content(project.name).and have_content(merge_request.source_project.name) end end - context "when the target branch is different from the project's default branch" do - let!(:merge_request) do - create(:merge_request, - source_project: project, - target_project: project, - source_branch: 'fix', - target_branch: 'feature_conflict') - end + context 'when project is public' do + set(:project) { create(:project, :public, :repository) } - before do - visit(project_merge_requests_path(project)) - end + context 'when not signed in' do + context "when the target branch is the project's default branch" do + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let!(:closed_merge_request) { create(:closed_merge_request, source_project: project, target_project: project) } - it 'shows target branch name' do - expect(page).to have_content(merge_request.target_branch) - end - end + before do + visit(project_merge_requests_path(project)) + end - context 'when a merge request has pipelines' do - let!(:build) { create :ci_build, pipeline: pipeline } + include_examples 'shows merge requests' - let(:merge_request) do - create(:merge_request_with_diffs, - source_project: project, - target_project: project, - source_branch: 'merge-test') - end + it 'shows open merge requests' do + expect(page).to have_content(merge_request.title).and have_no_content(closed_merge_request.title) + end - let(:pipeline) do - create(:ci_pipeline, - project: project, - sha: merge_request.diff_head_sha, - ref: merge_request.source_branch, - head_pipeline_of: merge_request) - end - - before do - project.enable_ci - - visit(project_merge_requests_path(project)) - end - - it 'shows pipeline status' do - page.within('.mr-list') do - expect(page).to have_link('Pipeline: pending') + it 'does not show target branch name' do + expect(page).to have_content(merge_request.title) + expect(find('.issuable-info')).not_to have_content(project.default_branch) + end end + + context "when the target branch is different from the project's default branch" do + let!(:merge_request) do + create(:merge_request, + source_project: project, + target_project: project, + source_branch: 'fix', + target_branch: 'feature_conflict') + end + + before do + visit(project_merge_requests_path(project)) + end + + it 'shows target branch name' do + expect(page).to have_content(merge_request.target_branch) + end + end + + context 'when a merge request has pipelines' do + let!(:build) { create :ci_build, pipeline: pipeline } + + let(:merge_request) do + create(:merge_request_with_diffs, + source_project: project, + target_project: project, + source_branch: 'merge-test') + end + + let(:pipeline) do + create(:ci_pipeline, + project: project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch, + head_pipeline_of: merge_request) + end + + before do + project.enable_ci + + visit(project_merge_requests_path(project)) + end + + it 'shows pipeline status' do + page.within('.mr-list') do + expect(page).to have_link('Pipeline: pending') + end + end + end + end + + context 'when signed in' do + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + + before do + project.add_developer(user) + sign_in(user) + + visit(project_merge_requests_path(project)) + end + + include_examples 'shows merge requests' + end + end + + context 'when project is internal' do + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + set(:project) { create(:project, :internal, :repository) } + + context 'when signed in' do + before do + project.add_developer(user) + sign_in(user) + + visit(project_merge_requests_path(project)) + end + + include_examples 'shows merge requests' end end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index cd3dc72d3c6..8e11cb94350 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -9,12 +9,14 @@ feature 'New project' do sign_in(user) end - it 'shows "New project" page' do + it 'shows "New project" page', :js do visit new_project_path expect(page).to have_content('Project path') expect(page).to have_content('Project name') + find('#import-project-tab').trigger('click') + expect(page).to have_link('GitHub') expect(page).to have_link('Bitbucket') expect(page).to have_link('GitLab.com') @@ -23,14 +25,15 @@ feature 'New project' do expect(page).to have_link('GitLab export') end - context 'Visibility level selector' do + context 'Visibility level selector', :js do Gitlab::VisibilityLevel.options.each do |key, level| it "sets selector to #{key}" do stub_application_setting(default_project_visibility: level) visit new_project_path - - expect(find_field("project_visibility_level_#{level}")).to be_checked + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{level}")).to be_checked + end end it "saves visibility level #{level} on validation error" do @@ -38,8 +41,9 @@ feature 'New project' do choose(s_(key)) click_button('Create project') - - expect(find_field("project_visibility_level_#{level}")).to be_checked + page.within('#blank-project-pane') do + expect(find_field("project_visibility_level_#{level}")).to be_checked + end end end end @@ -51,9 +55,11 @@ feature 'New project' do end it 'selects the user namespace' do - namespace = find('#project_namespace_id') + page.within('#blank-project-pane') do + namespace = find('#project_namespace_id') - expect(namespace.text).to eq user.username + expect(namespace.text).to eq user.username + end end end @@ -66,9 +72,11 @@ feature 'New project' do end it 'selects the group namespace' do - namespace = find('#project_namespace_id option[selected]') + page.within('#blank-project-pane') do + namespace = find('#project_namespace_id option[selected]') - expect(namespace.text).to eq group.name + expect(namespace.text).to eq group.name + end end end @@ -82,9 +90,11 @@ feature 'New project' do end it 'selects the group namespace' do - namespace = find('#project_namespace_id option[selected]') + page.within('#blank-project-pane') do + namespace = find('#project_namespace_id option[selected]') - expect(namespace.text).to eq subgroup.full_path + expect(namespace.text).to eq subgroup.full_path + end end end @@ -124,9 +134,10 @@ feature 'New project' do end end - context 'Import project options' do + context 'Import project options', :js do before do visit new_project_path + find('#import-project-tab').trigger('click') end context 'from git repository url' do diff --git a/spec/features/projects/user_views_details_spec.rb b/spec/features/projects/user_views_details_spec.rb new file mode 100644 index 00000000000..ffc063654cd --- /dev/null +++ b/spec/features/projects/user_views_details_spec.rb @@ -0,0 +1,151 @@ +require 'spec_helper' + +describe 'User views details' do + set(:user) { create(:user) } + + shared_examples_for 'redirects to the sign in page' do + it 'redirects to the sign in page' do + expect(current_path).to eq(new_user_session_path) + end + end + + shared_examples_for 'shows details of empty project' do + let(:user_has_ssh_key) { false } + + it 'shows details' do + expect(page).not_to have_content('Git global setup') + + page.all(:css, '.git-empty .clone').each do |element| + expect(element.text).to include(project.http_url_to_repo) + end + + expect(page).to have_field('project_clone', with: project.http_url_to_repo) unless user_has_ssh_key + end + end + + shared_examples_for 'shows details of non empty project' do + let(:user_has_ssh_key) { false } + + it 'shows details' do + page.within('.breadcrumbs .breadcrumb-item-text') do + expect(page).to have_content(project.title) + end + + expect(page).to have_field('project_clone', with: project.http_url_to_repo) unless user_has_ssh_key + end + end + + context 'when project is public' do + context 'when project is empty' do + set(:project) { create(:project_empty_repo, :public) } + + context 'when not signed in' do + before do + visit(project_path(project)) + end + + include_examples 'shows details of empty project' + end + + context 'when signed in' do + before do + sign_in(user) + end + + context 'when user does not have ssh keys' do + before do + visit(project_path(project)) + end + + include_examples 'shows details of empty project' + end + + context 'when user has ssh keys' do + before do + create(:personal_key, user: user) + + visit(project_path(project)) + end + + include_examples 'shows details of empty project' do + let(:user_has_ssh_key) { true } + end + end + end + end + + context 'when project is not empty' do + set(:project) { create(:project, :public, :repository) } + + before do + visit(project_path(project)) + end + + context 'when not signed in' do + before do + allow(Gitlab.config.gitlab).to receive(:host).and_return('www.example.com') + end + + include_examples 'shows details of non empty project' + end + + context 'when signed in' do + before do + sign_in(user) + end + + context 'when user does not have ssh keys' do + before do + visit(project_path(project)) + end + + include_examples 'shows details of non empty project' + end + + context 'when user has ssh keys' do + before do + create(:personal_key, user: user) + + visit(project_path(project)) + end + + include_examples 'shows details of non empty project' do + let(:user_has_ssh_key) { true } + end + end + end + end + end + + context 'when project is internal' do + set(:project) { create(:project, :internal, :repository) } + + context 'when not signed in' do + before do + visit(project_path(project)) + end + + include_examples 'redirects to the sign in page' + end + + context 'when signed in' do + before do + sign_in(user) + + visit(project_path(project)) + end + + include_examples 'shows details of non empty project' + end + end + + context 'when project is private' do + set(:project) { create(:project, :private) } + + before do + visit(project_path(project)) + end + + include_examples 'redirects to the sign in page' + end +end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index ac62280e4ca..3bc7ec3123f 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -12,8 +12,9 @@ feature 'Project' do visit new_project_path end - it "allows creation from templates" do - page.choose(template.name) + it "allows creation from templates", :js do + find('#create-from-template-tab').trigger('click') + find("##{template.name}").trigger('click') fill_in("project_path", with: template.name) page.within '#content-body' do diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 485b0b287ad..2dc3c5e3927 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Task Lists' do include Warden::Test::Helpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 10bc5f2ecd2..87ae1fa5660 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -57,15 +57,17 @@ describe ApplicationHelper do end describe 'project_icon' do + let(:asset_host) { 'http://assets' } + it 'returns an url for the avatar' do - project = create(:project, avatar: File.open(uploaded_image_temp_path)) + project = create(:project, :public, avatar: File.open(uploaded_image_temp_path)) avatar_url = "/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon(project.full_path).to_s) .to eq "" - allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host) - avatar_url = "#{gitlab_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif" + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + avatar_url = "#{asset_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon(project.full_path).to_s) .to eq "" diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 76e5964ccf7..97f0ed4904e 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe GroupsHelper do include ApplicationHelper + let(:asset_host) { 'http://assets' } + describe 'group_icon' do avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') @@ -10,14 +12,53 @@ describe GroupsHelper do group = create(:group) group.avatar = fixture_file_upload(avatar_file_path) group.save! - expect(group_icon(group.path).to_s) + + avatar_url = "/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif" + + expect(helper.group_icon(group).to_s) + .to eq "" + + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + avatar_url = "#{asset_host}/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif" + + expect(helper.group_icon(group).to_s) + .to eq "" + end + end + + describe 'group_icon_url' do + avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') + + it 'returns an url for the avatar' do + group = create(:group) + group.avatar = fixture_file_upload(avatar_file_path) + group.save! + expect(group_icon_url(group.path).to_s) + .to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif") + end + + it 'returns an CDN url for the avatar' do + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + group = create(:group) + group.avatar = fixture_file_upload(avatar_file_path) + group.save! + expect(group_icon_url(group.path).to_s) + .to match("#{asset_host}/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif") + end + + it 'returns an based url for the avatar if private' do + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + group = create(:group, :private) + group.avatar = fixture_file_upload(avatar_file_path) + group.save! + expect(group_icon_url(group.path).to_s) .to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif") end it 'gives default avatar_icon when no avatar is present' do group = create(:group) group.save! - expect(group_icon(group.path)).to match_asset_path('group_avatar.png') + expect(group_icon_url(group.path)).to match_asset_path('group_avatar.png') end end diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index a22b71fd1dc..268b5b83b73 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -28,7 +28,7 @@ import '~/lib/utils/common_utils'; preloadFixtures('merge_requests/diff_comment.html.raw'); beforeEach(function(done) { loadFixtures('merge_requests/diff_comment.html.raw'); - $('body').data('page', 'projects:merge_requests:show'); + $('body').attr('data-page', 'projects:merge_requests:show'); loadAwardsHandler(true).then((obj) => { awardsHandler = obj; spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb()); @@ -55,6 +55,9 @@ import '~/lib/utils/common_utils'; // restore original url root value gon.relative_url_root = urlRoot; + // Undo what we did to the shared + $('body').removeAttr('data-page'); + awardsHandler.destroy(); }); describe('::showEmojiMenu', function() { diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index f62bf43adb9..d5300d9c63d 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -19,6 +19,11 @@ describe('Quick Submit behavior', () => { this.textarea = $('.js-quick-submit textarea').first(); }); + afterEach(() => { + // Undo what we did to the shared + $('body').removeAttr('data-page'); + }); + it('does not respond to other keyCodes', () => { this.textarea.trigger(keydownEvent({ keyCode: 32, diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js new file mode 100644 index 00000000000..060ffaa339b --- /dev/null +++ b/spec/javascripts/flash_spec.js @@ -0,0 +1,269 @@ +import flash, { + createFlashEl, + createAction, + hideFlash, +} from '~/flash'; + +describe('Flash', () => { + describe('createFlashEl', () => { + let el; + + beforeEach(() => { + el = document.createElement('div'); + }); + + afterEach(() => { + el.innerHTML = ''; + }); + + it('creates flash element with type', () => { + el.innerHTML = createFlashEl('testing', 'alert'); + + expect( + el.querySelector('.flash-alert'), + ).not.toBeNull(); + }); + + it('escapes text', () => { + el.innerHTML = createFlashEl('', 'alert'); + + expect( + el.querySelector('.flash-text').textContent.trim(), + ).toBe(''); + }); + + it('adds container classes when inside content wrapper', () => { + el.innerHTML = createFlashEl('testing', 'alert', true); + + expect( + el.querySelector('.flash-text').classList.contains('container-fluid'), + ).toBeTruthy(); + expect( + el.querySelector('.flash-text').classList.contains('container-limited'), + ).toBeTruthy(); + }); + }); + + describe('hideFlash', () => { + let el; + + beforeEach(() => { + el = document.createElement('div'); + el.className = 'js-testing'; + }); + + it('sets transition style', () => { + hideFlash(el); + + expect( + el.style.transition, + ).toBe('opacity 0.3s'); + }); + + it('sets opacity style', () => { + hideFlash(el); + + expect( + el.style.opacity, + ).toBe('0'); + }); + + it('does not set styles when fadeTransition is false', () => { + hideFlash(el, false); + + expect( + el.style.opacity, + ).toBe(''); + expect( + el.style.transition, + ).toBe(''); + }); + + it('removes element after transitionend', () => { + document.body.appendChild(el); + + hideFlash(el); + el.dispatchEvent(new Event('transitionend')); + + expect( + document.querySelector('.js-testing'), + ).toBeNull(); + }); + + it('calls event listener callback once', () => { + spyOn(el, 'remove').and.callThrough(); + document.body.appendChild(el); + + hideFlash(el); + + el.dispatchEvent(new Event('transitionend')); + el.dispatchEvent(new Event('transitionend')); + + expect( + el.remove.calls.count(), + ).toBe(1); + }); + }); + + describe('createAction', () => { + let el; + + beforeEach(() => { + el = document.createElement('div'); + }); + + it('creates link with href', () => { + el.innerHTML = createAction({ + href: 'testing', + title: 'test', + }); + + expect( + el.querySelector('.flash-action').href, + ).toContain('testing'); + }); + + it('uses hash as href when no href is present', () => { + el.innerHTML = createAction({ + title: 'test', + }); + + expect( + el.querySelector('.flash-action').href, + ).toContain('#'); + }); + + it('adds role when no href is present', () => { + el.innerHTML = createAction({ + title: 'test', + }); + + expect( + el.querySelector('.flash-action').getAttribute('role'), + ).toBe('button'); + }); + + it('escapes the title text', () => { + el.innerHTML = createAction({ + title: '', + }); + + expect( + el.querySelector('.flash-action').textContent.trim(), + ).toBe(''); + }); + }); + + describe('createFlash', () => { + describe('no flash-container', () => { + it('does not add to the DOM', () => { + const flashEl = flash('testing'); + + expect( + flashEl, + ).toBeNull(); + expect( + document.querySelector('.flash-alert'), + ).toBeNull(); + }); + }); + + describe('with flash-container', () => { + beforeEach(() => { + document.body.innerHTML += ` +
+
+
+ `; + }); + + afterEach(() => { + document.querySelector('.js-content-wrapper').remove(); + }); + + it('adds flash element into container', () => { + flash('test'); + + expect( + document.querySelector('.flash-alert'), + ).not.toBeNull(); + }); + + it('adds flash into specified parent', () => { + flash( + 'test', + 'alert', + document.querySelector('.content-wrapper'), + ); + + expect( + document.querySelector('.content-wrapper .flash-alert'), + ).not.toBeNull(); + }); + + it('adds container classes when inside content-wrapper', () => { + flash('test'); + + expect( + document.querySelector('.flash-text').className, + ).toBe('flash-text container-fluid container-limited'); + }); + + it('does not add container when outside of content-wrapper', () => { + document.querySelector('.content-wrapper').className = 'js-content-wrapper'; + flash('test'); + + expect( + document.querySelector('.flash-text').className.trim(), + ).toBe('flash-text'); + }); + + it('removes element after clicking', () => { + flash('test', 'alert', document, null, false); + + document.querySelector('.flash-alert').click(); + + expect( + document.querySelector('.flash-alert'), + ).toBeNull(); + }); + + describe('with actionConfig', () => { + it('adds action link', () => { + flash( + 'test', + 'alert', + document, + { + title: 'test', + }, + ); + + expect( + document.querySelector('.flash-action'), + ).not.toBeNull(); + }); + + it('calls actionConfig clickHandler on click', () => { + const actionConfig = { + title: 'test', + clickHandler: jasmine.createSpy('actionConfig'), + }; + + flash( + 'test', + 'alert', + document, + actionConfig, + ); + + document.querySelector('.flash-action').click(); + + expect( + actionConfig.clickHandler, + ).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index fa24aa426b6..2779686a6f5 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -1,110 +1,108 @@ /* eslint-disable space-before-function-paren, arrow-body-style */ -import '~/gl_field_errors'; +import GlFieldErrors from '~/gl_field_errors'; -((global) => { +describe('GL Style Field Errors', function() { preloadFixtures('static/gl_field_errors.html.raw'); - describe('GL Style Field Errors', function() { - beforeEach(function() { - loadFixtures('static/gl_field_errors.html.raw'); - const $form = this.$form = $('form.gl-show-field-errors'); - this.fieldErrors = new global.GlFieldErrors($form); - }); - - it('should select the correct input elements', function() { - expect(this.$form).toBeDefined(); - expect(this.$form.length).toBe(1); - expect(this.fieldErrors).toBeDefined(); - const inputs = this.fieldErrors.state.inputs; - expect(inputs.length).toBe(4); - }); - - it('should ignore elements with custom error handling', function() { - const customErrorFlag = 'gl-field-error-ignore'; - const customErrorElem = $(`.${customErrorFlag}`); - - expect(customErrorElem.length).toBe(1); - - const customErrors = this.fieldErrors.state.inputs.filter((input) => { - return input.inputElement.hasClass(customErrorFlag); - }); - expect(customErrors.length).toBe(0); - }); - - it('should not show any errors before submit attempt', function() { - this.$form.find('.email').val('not-a-valid-email').keyup(); - this.$form.find('.text-required').val('').keyup(); - this.$form.find('.alphanumberic').val('?---*').keyup(); - - const errorsShown = this.$form.find('.gl-field-error-outline'); - expect(errorsShown.length).toBe(0); - }); - - it('should show errors when input valid is submitted', function() { - this.$form.find('.email').val('not-a-valid-email').keyup(); - this.$form.find('.text-required').val('').keyup(); - this.$form.find('.alphanumberic').val('?---*').keyup(); - - this.$form.submit(); - - const errorsShown = this.$form.find('.gl-field-error-outline'); - expect(errorsShown.length).toBe(4); - }); - - it('should properly track validity state on input after invalid submission attempt', function() { - this.$form.submit(); - - const emailInputModel = this.fieldErrors.state.inputs[1]; - const fieldState = emailInputModel.state; - const emailInputElement = emailInputModel.inputElement; - - // No input - expect(emailInputElement).toHaveClass('gl-field-error-outline'); - expect(fieldState.empty).toBe(true); - expect(fieldState.valid).toBe(false); - - // Then invalid input - emailInputElement.val('not-a-valid-email').keyup(); - expect(emailInputElement).toHaveClass('gl-field-error-outline'); - expect(fieldState.empty).toBe(false); - expect(fieldState.valid).toBe(false); - - // Then valid input - emailInputElement.val('email@gitlab.com').keyup(); - expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); - expect(fieldState.empty).toBe(false); - expect(fieldState.valid).toBe(true); - - // Then invalid input - emailInputElement.val('not-a-valid-email').keyup(); - expect(emailInputElement).toHaveClass('gl-field-error-outline'); - expect(fieldState.empty).toBe(false); - expect(fieldState.valid).toBe(false); - - // Then empty input - emailInputElement.val('').keyup(); - expect(emailInputElement).toHaveClass('gl-field-error-outline'); - expect(fieldState.empty).toBe(true); - expect(fieldState.valid).toBe(false); - - // Then valid input - emailInputElement.val('email@gitlab.com').keyup(); - expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); - expect(fieldState.empty).toBe(false); - expect(fieldState.valid).toBe(true); - }); - - it('should properly infer error messages', function() { - this.$form.submit(); - const trackedInputs = this.fieldErrors.state.inputs; - const inputHasTitle = trackedInputs[1]; - const hasTitleErrorElem = inputHasTitle.inputElement.siblings('.gl-field-error'); - const inputNoTitle = trackedInputs[2]; - const noTitleErrorElem = inputNoTitle.inputElement.siblings('.gl-field-error'); - - expect(noTitleErrorElem.text()).toBe('This field is required.'); - expect(hasTitleErrorElem.text()).toBe('Please provide a valid email address.'); - }); + beforeEach(function() { + loadFixtures('static/gl_field_errors.html.raw'); + const $form = this.$form = $('form.gl-show-field-errors'); + this.fieldErrors = new GlFieldErrors($form); }); -})(window.gl || (window.gl = {})); + + it('should select the correct input elements', function() { + expect(this.$form).toBeDefined(); + expect(this.$form.length).toBe(1); + expect(this.fieldErrors).toBeDefined(); + const inputs = this.fieldErrors.state.inputs; + expect(inputs.length).toBe(4); + }); + + it('should ignore elements with custom error handling', function() { + const customErrorFlag = 'gl-field-error-ignore'; + const customErrorElem = $(`.${customErrorFlag}`); + + expect(customErrorElem.length).toBe(1); + + const customErrors = this.fieldErrors.state.inputs.filter((input) => { + return input.inputElement.hasClass(customErrorFlag); + }); + expect(customErrors.length).toBe(0); + }); + + it('should not show any errors before submit attempt', function() { + this.$form.find('.email').val('not-a-valid-email').keyup(); + this.$form.find('.text-required').val('').keyup(); + this.$form.find('.alphanumberic').val('?---*').keyup(); + + const errorsShown = this.$form.find('.gl-field-error-outline'); + expect(errorsShown.length).toBe(0); + }); + + it('should show errors when input valid is submitted', function() { + this.$form.find('.email').val('not-a-valid-email').keyup(); + this.$form.find('.text-required').val('').keyup(); + this.$form.find('.alphanumberic').val('?---*').keyup(); + + this.$form.submit(); + + const errorsShown = this.$form.find('.gl-field-error-outline'); + expect(errorsShown.length).toBe(4); + }); + + it('should properly track validity state on input after invalid submission attempt', function() { + this.$form.submit(); + + const emailInputModel = this.fieldErrors.state.inputs[1]; + const fieldState = emailInputModel.state; + const emailInputElement = emailInputModel.inputElement; + + // No input + expect(emailInputElement).toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(true); + expect(fieldState.valid).toBe(false); + + // Then invalid input + emailInputElement.val('not-a-valid-email').keyup(); + expect(emailInputElement).toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(false); + expect(fieldState.valid).toBe(false); + + // Then valid input + emailInputElement.val('email@gitlab.com').keyup(); + expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(false); + expect(fieldState.valid).toBe(true); + + // Then invalid input + emailInputElement.val('not-a-valid-email').keyup(); + expect(emailInputElement).toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(false); + expect(fieldState.valid).toBe(false); + + // Then empty input + emailInputElement.val('').keyup(); + expect(emailInputElement).toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(true); + expect(fieldState.valid).toBe(false); + + // Then valid input + emailInputElement.val('email@gitlab.com').keyup(); + expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); + expect(fieldState.empty).toBe(false); + expect(fieldState.valid).toBe(true); + }); + + it('should properly infer error messages', function() { + this.$form.submit(); + const trackedInputs = this.fieldErrors.state.inputs; + const inputHasTitle = trackedInputs[1]; + const hasTitleErrorElem = inputHasTitle.inputElement.siblings('.gl-field-error'); + const inputNoTitle = trackedInputs[2]; + const noTitleErrorElem = inputNoTitle.inputElement.siblings('.gl-field-error'); + + expect(noTitleErrorElem.text()).toBe('This field is required.'); + expect(hasTitleErrorElem.text()).toBe('Please provide a valid email address.'); + }); +}); diff --git a/spec/javascripts/gl_form_spec.js b/spec/javascripts/gl_form_spec.js index 837feacec1d..124fc030774 100644 --- a/spec/javascripts/gl_form_spec.js +++ b/spec/javascripts/gl_form_spec.js @@ -1,18 +1,11 @@ import autosize from 'vendor/autosize'; -import '~/gl_form'; +import GLForm from '~/gl_form'; import '~/lib/utils/text_utility'; import '~/lib/utils/common_utils'; window.autosize = autosize; describe('GLForm', () => { - const global = window.gl || (window.gl = {}); - const GLForm = global.GLForm; - - it('should be defined in the global scope', () => { - expect(GLForm).toBeDefined(); - }); - describe('when instantiated', function () { beforeEach((done) => { this.form = $('
`; setFixtures(diffsResponse.html + noteFormHtml); - $('body').data('page', 'projects:merge_requests:show'); + $('body').attr('data-page', 'projects:merge_requests:show'); window.gon.current_user_id = $('.note:last').data('author-id'); return new Notes('', []); }); + afterEach(() => { + // Undo what we did to the shared + $('body').removeAttr('data-page'); + }); + describe('up arrow', () => { it('edits last comment in discussion when triggered in discussion form', (done) => { const upArrowEvent = $.Event('keydown'); @@ -84,7 +94,7 @@ describe('Merge request notes', () => { spyOnEvent('.note:last .js-note-edit', 'click'); - $('.js-discussion-reply-button').click(); + $('.js-discussion-reply-button').trigger('click'); setTimeout(() => { $('.js-note-text').trigger(upArrowEvent); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index ccdbfcba692..18916c5aa97 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -277,7 +277,7 @@ import 'vendor/jquery.scrollTo'; describe('loadDiff', function () { beforeEach(() => { loadFixtures('merge_requests/diff_comment.html.raw'); - spyOn(window.gl.utils, 'getPagePath').and.returnValue('merge_requests'); + $('body').attr('data-page', 'projects:merge_requests:show'); window.gl.ImageFile = () => {}; window.notes = new Notes('', []); spyOn(window.notes, 'toggleDiffNote').and.callThrough(); @@ -286,6 +286,9 @@ import 'vendor/jquery.scrollTo'; afterEach(() => { delete window.gl.ImageFile; delete window.notes; + + // Undo what we did to the shared + $('body').removeAttr('data-page'); }); it('requires an absolute pathname', function () { diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 3e791a31604..66c52611614 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -39,7 +39,12 @@ import '~/notes'; loadFixtures(commentsTemplate); gl.utils.disableButtonIfEmptyField = _.noop; window.project_uploads_path = 'http://test.host/uploads'; - $('body').data('page', 'projects:merge_requets:show'); + $('body').attr('data-page', 'projects:merge_requets:show'); + }); + + afterEach(() => { + // Undo what we did to the shared + $('body').removeAttr('data-page'); }); describe('task lists', function() { @@ -426,19 +431,17 @@ import '~/notes'; }); describe('putEditFormInPlace', () => { - it('should call gl.GLForm with GFM parameter passed through', () => { - spyOn(gl, 'GLForm'); + it('should call GLForm with GFM parameter passed through', () => { + const notes = new Notes('', []); + const $el = $(` +
+
+
+ `); - const $el = jasmine.createSpyObj('$form', ['find', 'closest']); - $el.find.and.returnValue($('
')); - $el.closest.and.returnValue($('
')); + notes.putEditFormInPlace($el); - Notes.prototype.putEditFormInPlace.call({ - getEditFormSelector: () => '', - enableGFM: true - }, $el); - - expect(gl.GLForm).toHaveBeenCalledWith(jasmine.any(Object), true); + expect(notes.glForm.enableGFM).toBeTruthy(); }); }); @@ -815,7 +818,7 @@ import '~/notes'; }); it('shows a flash message', () => { - this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline); + this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); expect($('.flash-alert').is(':visible')).toBeTruthy(); }); @@ -828,7 +831,7 @@ import '~/notes'; }); it('hides visible flash message', () => { - this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline); + this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); this.notes.clearFlash(); diff --git a/spec/javascripts/pipeline_schedules/setup_pipeline_variable_list_spec.js b/spec/javascripts/pipeline_schedules/setup_pipeline_variable_list_spec.js index e90c20e20d2..5b316b319a5 100644 --- a/spec/javascripts/pipeline_schedules/setup_pipeline_variable_list_spec.js +++ b/spec/javascripts/pipeline_schedules/setup_pipeline_variable_list_spec.js @@ -92,7 +92,7 @@ describe('Pipeline Variable List', () => { }); it('should remove the row when clicking the remove button', () => { - $markup.find('.js-row-remove-button').click(); + $markup.find('.js-row-remove-button').trigger('click'); expect($markup.find('.js-row').length).toBe(0); }); diff --git a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js index 2b3a821dbd9..b24567ffc0c 100644 --- a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js +++ b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js @@ -109,12 +109,16 @@ describe('PrometheusMetrics', () => { it('should show loader animation while response is being loaded and hide it when request is complete', (done) => { const deferred = $.Deferred(); - spyOn($, 'getJSON').and.returnValue(deferred.promise()); + spyOn($, 'ajax').and.returnValue(deferred.promise()); prometheusMetrics.loadActiveMetrics(); expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeFalsy(); - expect($.getJSON).toHaveBeenCalledWith(prometheusMetrics.activeMetricsEndpoint); + expect($.ajax).toHaveBeenCalledWith({ + url: prometheusMetrics.activeMetricsEndpoint, + dataType: 'json', + global: false, + }); deferred.resolve({ data: metrics, success: true }); @@ -126,7 +130,7 @@ describe('PrometheusMetrics', () => { it('should show empty state if response failed to load', (done) => { const deferred = $.Deferred(); - spyOn($, 'getJSON').and.returnValue(deferred.promise()); + spyOn($, 'ajax').and.returnValue(deferred.promise()); spyOn(prometheusMetrics, 'populateActiveMetrics'); prometheusMetrics.loadActiveMetrics(); @@ -142,7 +146,7 @@ describe('PrometheusMetrics', () => { it('should populate metrics list once response is loaded', (done) => { const deferred = $.Deferred(); - spyOn($, 'getJSON').and.returnValue(deferred.promise()); + spyOn($, 'ajax').and.returnValue(deferred.promise()); spyOn(prometheusMetrics, 'populateActiveMetrics'); prometheusMetrics.loadActiveMetrics(); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index a53f58b5d0d..cf811af3d6c 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -6,7 +6,7 @@ import '~/lib/utils/common_utils'; import 'vendor/fuzzaldrin-plus'; (function() { - var addBodyAttributes, assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget; + var assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget; var userName = 'root'; widget = null; @@ -29,25 +29,31 @@ import 'vendor/fuzzaldrin-plus'; groupName = 'Gitlab Org'; - // Add required attributes to body before starting the test. - // section would be dashboard|group|project - addBodyAttributes = function(section) { - var $body; - if (section == null) { - section = 'dashboard'; - } - $body = $('body'); + const removeBodyAttributes = function() { + const $body = $('body'); + $body.removeAttr('data-page'); $body.removeAttr('data-project'); $body.removeAttr('data-group'); + }; + + // Add required attributes to body before starting the test. + // section would be dashboard|group|project + const addBodyAttributes = function(section) { + if (section == null) { + section = 'dashboard'; + } + + const $body = $('body'); + removeBodyAttributes(); switch (section) { case 'dashboard': - return $body.data('page', 'root:index'); + return $body.attr('data-page', 'root:index'); case 'group': - $body.data('page', 'groups:show'); + $body.attr('data-page', 'groups:show'); return $body.data('group', 'gitlab-org'); case 'project': - $body.data('page', 'projects:show'); + $body.attr('data-page', 'projects:show'); return $body.data('project', 'gitlab-ce'); } }; @@ -108,7 +114,7 @@ import 'vendor/fuzzaldrin-plus'; preloadFixtures('static/search_autocomplete.html.raw'); beforeEach(function() { loadFixtures('static/search_autocomplete.html.raw'); - widget = new gl.SearchAutocomplete; + // Prevent turbolinks from triggering within gl_dropdown spyOn(window.gl.utils, 'visitUrl').and.returnValue(true); @@ -120,6 +126,8 @@ import 'vendor/fuzzaldrin-plus'; }); afterEach(function() { + // Undo what we did to the shared + removeBodyAttributes(); window.gon = {}; }); it('should show Dashboard specific dropdown menu', function() { diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js index e924107b29b..29b15f3a782 100644 --- a/spec/javascripts/u2f/authenticate_spec.js +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -1,72 +1,63 @@ -/* eslint-disable space-before-function-paren, new-parens, quotes, comma-dangle, no-var, one-var, one-var-declaration-per-line, max-len */ -/* global MockU2FDevice */ -/* global U2FAuthenticate */ - -import '~/u2f/authenticate'; -import '~/u2f/util'; -import '~/u2f/error'; +import U2FAuthenticate from '~/u2f/authenticate'; import 'vendor/u2f'; -import './mock_u2f_device'; +import MockU2FDevice from './mock_u2f_device'; -(function() { - describe('U2FAuthenticate', function() { - preloadFixtures('u2f/authenticate.html.raw'); +describe('U2FAuthenticate', () => { + preloadFixtures('u2f/authenticate.html.raw'); - beforeEach(function() { - loadFixtures('u2f/authenticate.html.raw'); - this.u2fDevice = new MockU2FDevice; - this.container = $("#js-authenticate-u2f"); - this.component = new window.gl.U2FAuthenticate( - this.container, - '#js-login-u2f-form', - { - sign_requests: [] - }, - document.querySelector('#js-login-2fa-device'), - document.querySelector('.js-2fa-form') - ); + beforeEach(() => { + loadFixtures('u2f/authenticate.html.raw'); + this.u2fDevice = new MockU2FDevice(); + this.container = $('#js-authenticate-u2f'); + this.component = new U2FAuthenticate( + this.container, + '#js-login-u2f-form', + { + sign_requests: [], + }, + document.querySelector('#js-login-2fa-device'), + document.querySelector('.js-2fa-form'), + ); - // bypass automatic form submission within renderAuthenticated - spyOn(this.component, 'renderAuthenticated').and.returnValue(true); + // bypass automatic form submission within renderAuthenticated + spyOn(this.component, 'renderAuthenticated').and.returnValue(true); - return this.component.start(); + return this.component.start(); + }); + + it('allows authenticating via a U2F device', () => { + const inProgressMessage = this.container.find('p'); + expect(inProgressMessage.text()).toContain('Trying to communicate with your device'); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: 'this is data from the device', }); - it('allows authenticating via a U2F device', function() { - var inProgressMessage; - inProgressMessage = this.container.find("p"); - expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + }); + + return describe('errors', () => { + it('displays an error message', () => { + const setupButton = this.container.find('#js-login-u2f-device'); + setupButton.trigger('click'); this.u2fDevice.respondToAuthenticateRequest({ - deviceData: "this is data from the device" + errorCode: 'error!', + }); + const errorMessage = this.container.find('p'); + return expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + }); + return it('allows retrying authentication after an error', () => { + let setupButton = this.container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + errorCode: 'error!', + }); + const retryButton = this.container.find('#js-u2f-try-again'); + retryButton.trigger('click'); + setupButton = this.container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: 'this is data from the device', }); expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); }); - return describe("errors", function() { - it("displays an error message", function() { - var errorMessage, setupButton; - setupButton = this.container.find("#js-login-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToAuthenticateRequest({ - errorCode: "error!" - }); - errorMessage = this.container.find("p"); - return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); - }); - return it("allows retrying authentication after an error", function() { - var retryButton, setupButton; - setupButton = this.container.find("#js-login-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToAuthenticateRequest({ - errorCode: "error!" - }); - retryButton = this.container.find("#js-u2f-try-again"); - retryButton.click(); - setupButton = this.container.find("#js-login-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToAuthenticateRequest({ - deviceData: "this is data from the device" - }); - expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); - }); - }); }); -}).call(window); +}); diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js index 4eb8ad3d9e4..5a1ace2b4d6 100644 --- a/spec/javascripts/u2f/mock_u2f_device.js +++ b/spec/javascripts/u2f/mock_u2f_device.js @@ -1,31 +1,28 @@ -/* eslint-disable space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign, max-len */ +/* eslint-disable prefer-rest-params, wrap-iife, +no-unused-expressions, no-return-assign, no-param-reassign*/ -(function() { - this.MockU2FDevice = (function() { - function MockU2FDevice() { - this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this); - this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this); - window.u2f || (window.u2f = {}); - window.u2f.register = (function(_this) { - return function(appId, registerRequests, signRequests, callback) { - return _this.registerCallback = callback; - }; - })(this); - window.u2f.sign = (function(_this) { - return function(appId, challenges, signRequests, callback) { - return _this.authenticateCallback = callback; - }; - })(this); - } +export default class MockU2FDevice { + constructor() { + this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this); + this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this); + window.u2f || (window.u2f = {}); + window.u2f.register = (function (_this) { + return function (appId, registerRequests, signRequests, callback) { + return _this.registerCallback = callback; + }; + })(this); + window.u2f.sign = (function (_this) { + return function (appId, challenges, signRequests, callback) { + return _this.authenticateCallback = callback; + }; + })(this); + } - MockU2FDevice.prototype.respondToRegisterRequest = function(params) { - return this.registerCallback(params); - }; + respondToRegisterRequest(params) { + return this.registerCallback(params); + } - MockU2FDevice.prototype.respondToAuthenticateRequest = function(params) { - return this.authenticateCallback(params); - }; - - return MockU2FDevice; - })(); -}).call(window); + respondToAuthenticateRequest(params) { + return this.authenticateCallback(params); + } +} diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js index f5e32c1b358..b0051f11362 100644 --- a/spec/javascripts/u2f/register_spec.js +++ b/spec/javascripts/u2f/register_spec.js @@ -1,77 +1,69 @@ -/* eslint-disable space-before-function-paren, new-parens, quotes, no-var, one-var, one-var-declaration-per-line, comma-dangle, max-len */ -/* global MockU2FDevice */ -/* global U2FRegister */ - -import '~/u2f/register'; -import '~/u2f/util'; -import '~/u2f/error'; +import U2FRegister from '~/u2f/register'; import 'vendor/u2f'; -import './mock_u2f_device'; +import MockU2FDevice from './mock_u2f_device'; -(function() { - describe('U2FRegister', function() { - preloadFixtures('u2f/register.html.raw'); +describe('U2FRegister', () => { + preloadFixtures('u2f/register.html.raw'); - beforeEach(function() { - loadFixtures('u2f/register.html.raw'); - this.u2fDevice = new MockU2FDevice; - this.container = $("#js-register-u2f"); - this.component = new U2FRegister(this.container, $("#js-register-u2f-templates"), {}, "token"); - return this.component.start(); + beforeEach(() => { + loadFixtures('u2f/register.html.raw'); + this.u2fDevice = new MockU2FDevice(); + this.container = $('#js-register-u2f'); + this.component = new U2FRegister(this.container, $('#js-register-u2f-templates'), {}, 'token'); + return this.component.start(); + }); + + it('allows registering a U2F device', () => { + const setupButton = this.container.find('#js-setup-u2f-device'); + expect(setupButton.text()).toBe('Setup new U2F device'); + setupButton.trigger('click'); + const inProgressMessage = this.container.children('p'); + expect(inProgressMessage.text()).toContain('Trying to communicate with your device'); + this.u2fDevice.respondToRegisterRequest({ + deviceData: 'this is data from the device', }); - it('allows registering a U2F device', function() { - var deviceResponse, inProgressMessage, registeredMessage, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - expect(setupButton.text()).toBe('Setup new U2F device'); - setupButton.click(); - inProgressMessage = this.container.children("p"); - expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + const registeredMessage = this.container.find('p'); + const deviceResponse = this.container.find('#js-device-response'); + expect(registeredMessage.text()).toContain('Your device was successfully set up!'); + return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + }); + + return describe('errors', () => { + it('doesn\'t allow the same device to be registered twice (for the same user', () => { + const setupButton = this.container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); this.u2fDevice.respondToRegisterRequest({ - deviceData: "this is data from the device" + errorCode: 4, }); - registeredMessage = this.container.find('p'); - deviceResponse = this.container.find('#js-device-response'); - expect(registeredMessage.text()).toContain("Your device was successfully set up!"); - return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + const errorMessage = this.container.find('p'); + return expect(errorMessage.text()).toContain('already been registered with us'); }); - return describe("errors", function() { - it("doesn't allow the same device to be registered twice (for the same user", function() { - var errorMessage, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToRegisterRequest({ - errorCode: 4 - }); - errorMessage = this.container.find("p"); - return expect(errorMessage.text()).toContain("already been registered with us"); + + it('displays an error message for other errors', () => { + const setupButton = this.container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: 'error!', }); - it("displays an error message for other errors", function() { - var errorMessage, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToRegisterRequest({ - errorCode: "error!" - }); - errorMessage = this.container.find("p"); - return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); + const errorMessage = this.container.find('p'); + return expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + }); + + return it('allows retrying registration after an error', () => { + let setupButton = this.container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: 'error!', }); - return it("allows retrying registration after an error", function() { - var registeredMessage, retryButton, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToRegisterRequest({ - errorCode: "error!" - }); - retryButton = this.container.find("#U2FTryAgain"); - retryButton.click(); - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.click(); - this.u2fDevice.respondToRegisterRequest({ - deviceData: "this is data from the device" - }); - registeredMessage = this.container.find("p"); - return expect(registeredMessage.text()).toContain("Your device was successfully set up!"); + const retryButton = this.container.find('#U2FTryAgain'); + retryButton.trigger('click'); + setupButton = this.container.find('#js-setup-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + deviceData: 'this is data from the device', }); + const registeredMessage = this.container.find('p'); + return expect(registeredMessage.text()).toContain('Your device was successfully set up!'); }); }); -}).call(window); +}); diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb index 695fd6f8573..8e524f9b05a 100644 --- a/spec/lib/gitlab/file_detector_spec.rb +++ b/spec/lib/gitlab/file_detector_spec.rb @@ -18,6 +18,10 @@ describe Gitlab::FileDetector do expect(described_class.type_of('README.md')).to eq(:readme) end + it 'returns nil for a README file in a directory' do + expect(described_class.type_of('foo/README.md')).to be_nil + end + it 'returns the type of a changelog file' do %w(CHANGELOG HISTORY CHANGES NEWS).each do |file| expect(described_class.type_of(file)).to eq(:changelog) @@ -52,6 +56,14 @@ describe Gitlab::FileDetector do end end + it 'returns the type of an issue template' do + expect(described_class.type_of('.gitlab/issue_templates/foo.md')).to eq(:issue_template) + end + + it 'returns the type of a merge request template' do + expect(described_class.type_of('.gitlab/merge_request_templates/foo.md')).to eq(:merge_request_template) + end + it 'returns nil for an unknown file' do expect(described_class.type_of('foo.txt')).to be_nil end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 1ee4acfd193..b11fa38856b 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -68,31 +68,52 @@ describe Gitlab::Git::Repository, seed_helper: true do expect { broken_repo.rugged }.to raise_error(Gitlab::Git::Repository::NoRepository) end - context 'with no Git env stored' do - before do - expect(Gitlab::Git::Env).to receive(:all).and_return({}) + describe 'alternates keyword argument' do + context 'with no Git env stored' do + before do + allow(Gitlab::Git::Env).to receive(:all).and_return({}) + end + + it "is passed an empty array" do + expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: []) + + repository.rugged + end end - it "whitelist some variables and pass them via the alternates keyword argument" do - expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: []) + context 'with absolute and relative Git object dir envvars stored' do + before do + allow(Gitlab::Git::Env).to receive(:all).and_return({ + 'GIT_OBJECT_DIRECTORY_RELATIVE' => './objects/foo', + 'GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE' => ['./objects/bar', './objects/baz'], + 'GIT_OBJECT_DIRECTORY' => 'ignored', + 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => %w[ignored ignored], + 'GIT_OTHER' => 'another_env' + }) + end - repository.rugged - end - end + it "is passed the relative object dir envvars after being converted to absolute ones" do + alternates = %w[foo bar baz].map { |d| File.join(repository.path, './objects', d) } + expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: alternates) - context 'with some Git env stored' do - before do - expect(Gitlab::Git::Env).to receive(:all).and_return({ - 'GIT_OBJECT_DIRECTORY' => 'foo', - 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar', - 'GIT_OTHER' => 'another_env' - }) + repository.rugged + end end - it "whitelist some variables and pass them via the alternates keyword argument" do - expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: %w[foo bar]) + context 'with only absolute Git object dir envvars stored' do + before do + allow(Gitlab::Git::Env).to receive(:all).and_return({ + 'GIT_OBJECT_DIRECTORY' => 'foo', + 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => %w[bar baz], + 'GIT_OTHER' => 'another_env' + }) + end - repository.rugged + it "is passed the absolute object dir envvars as is" do + expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: %w[foo bar baz]) + + repository.rugged + end end end end @@ -1489,6 +1510,21 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#fetch' do + let(:git_path) { Gitlab.config.git.bin_path } + let(:remote_name) { 'my_remote' } + + subject { repository.fetch(remote_name) } + + it 'fetches the remote and returns true if the command was successful' do + expect(repository).to receive(:popen) + .with(%W(#{git_path} fetch #{remote_name}), repository.path) + .and_return(['', 0]) + + expect(subject).to be(true) + end + end + def create_remote_branch(repository, remote_name, branch_name, source_branch_name) source_branch = repository.branches.find { |branch| branch.name == source_branch_name } rugged = repository.rugged diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb index fd5f984601e..cbc7ce1c1b0 100644 --- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -73,4 +73,15 @@ describe Gitlab::GitalyClient::RepositoryService do client.apply_gitattributes(revision) end end + + describe '#has_local_branches?' do + it 'sends a has_local_branches message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:has_local_branches) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(value: true)) + + expect(client.has_local_branches?).to be(true) + end + end end diff --git a/spec/lib/gitlab/gitaly_client/util_spec.rb b/spec/lib/gitlab/gitaly_client/util_spec.rb index 498f6886bee..c0c29552494 100644 --- a/spec/lib/gitlab/gitaly_client/util_spec.rb +++ b/spec/lib/gitlab/gitaly_client/util_spec.rb @@ -6,16 +6,16 @@ describe Gitlab::GitalyClient::Util do let(:relative_path) { 'my/repo.git' } let(:gl_repository) { 'project-1' } let(:git_object_directory) { '.git/objects' } - let(:git_alternate_object_directory) { '/dir/one:/dir/two' } + let(:git_alternate_object_directory) { ['/dir/one', '/dir/two'] } subject do described_class.repository(repository_storage, relative_path, gl_repository) end it 'creates a Gitaly::Repository with the given data' do - expect(Gitlab::Git::Env).to receive(:[]).with('GIT_OBJECT_DIRECTORY') + allow(Gitlab::Git::Env).to receive(:[]).with('GIT_OBJECT_DIRECTORY_RELATIVE') .and_return(git_object_directory) - expect(Gitlab::Git::Env).to receive(:[]).with('GIT_ALTERNATE_OBJECT_DIRECTORIES') + allow(Gitlab::Git::Env).to receive(:[]).with('GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE') .and_return(git_alternate_object_directory) expect(subject).to be_a(Gitaly::Repository) @@ -23,7 +23,7 @@ describe Gitlab::GitalyClient::Util do expect(subject.relative_path).to eq(relative_path) expect(subject.gl_repository).to eq(gl_repository) expect(subject.git_object_directory).to eq(git_object_directory) - expect(subject.git_alternate_object_directories).to eq([git_alternate_object_directory]) + expect(subject.git_alternate_object_directories).to eq(git_alternate_object_directory) end end diff --git a/spec/lib/gitlab/import_export/project.group.json b/spec/lib/gitlab/import_export/project.group.json new file mode 100644 index 00000000000..82a1fbd2fc5 --- /dev/null +++ b/spec/lib/gitlab/import_export/project.group.json @@ -0,0 +1,188 @@ +{ + "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", + "visibility_level": 10, + "archived": false, + "milestones": [ + { + "id": 1, + "title": "Project milestone", + "project_id": 8, + "description": "Project-level milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": null + } + ], + "labels": [ + { + "id": 2, + "title": "project label", + "color": "#428bca", + "project_id": 8, + "created_at": "2016-07-22T08:55:44.161Z", + "updated_at": "2016-07-22T08:55:44.161Z", + "template": false, + "description": "", + "type": "ProjectLabel", + "priorities": [ + { + "id": 1, + "project_id": 5, + "label_id": 1, + "priority": 1, + "created_at": "2016-10-18T09:35:43.338Z", + "updated_at": "2016-10-18T09:35:43.338Z" + } + ] + } + ], + "issues": [ + { + "id": 1, + "title": "Fugiat est minima quae maxime non similique.", + "assignee_id": null, + "project_id": 8, + "author_id": 1, + "created_at": "2017-07-07T18:13:01.138Z", + "updated_at": "2017-08-15T18:37:40.807Z", + "branch_name": null, + "description": "Quam totam fuga numquam in eveniet.", + "state": "opened", + "iid": 1, + "updated_by_id": 1, + "confidential": false, + "deleted_at": null, + "due_date": null, + "moved_to_id": null, + "lock_version": null, + "time_estimate": 0, + "closed_at": null, + "last_edited_at": null, + "last_edited_by_id": null, + "group_milestone_id": null, + "milestone": { + "id": 1, + "title": "Project milestone", + "project_id": 8, + "description": "Project-level milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": null + }, + "label_links": [ + { + "id": 11, + "label_id": 6, + "target_id": 1, + "target_type": "Issue", + "created_at": "2017-08-15T18:37:40.795Z", + "updated_at": "2017-08-15T18:37:40.795Z", + "label": { + "id": 6, + "title": "group label", + "color": "#A8D695", + "project_id": null, + "created_at": "2017-08-15T18:37:19.698Z", + "updated_at": "2017-08-15T18:37:19.698Z", + "template": false, + "description": "", + "group_id": 5, + "type": "GroupLabel", + "priorities": [] + } + }, + { + "id": 11, + "label_id": 2, + "target_id": 1, + "target_type": "Issue", + "created_at": "2017-08-15T18:37:40.795Z", + "updated_at": "2017-08-15T18:37:40.795Z", + "label": { + "id": 6, + "title": "project label", + "color": "#A8D695", + "project_id": null, + "created_at": "2017-08-15T18:37:19.698Z", + "updated_at": "2017-08-15T18:37:19.698Z", + "template": false, + "description": "", + "group_id": 5, + "type": "ProjectLabel", + "priorities": [] + } + } + ] + }, + { + "id": 2, + "title": "Fugiat est minima quae maxime non similique.", + "assignee_id": null, + "project_id": 8, + "author_id": 1, + "created_at": "2017-07-07T18:13:01.138Z", + "updated_at": "2017-08-15T18:37:40.807Z", + "branch_name": null, + "description": "Quam totam fuga numquam in eveniet.", + "state": "opened", + "iid": 2, + "updated_by_id": 1, + "confidential": false, + "deleted_at": null, + "due_date": null, + "moved_to_id": null, + "lock_version": null, + "time_estimate": 0, + "closed_at": null, + "last_edited_at": null, + "last_edited_by_id": null, + "group_milestone_id": null, + "milestone": { + "id": 2, + "title": "A group milestone", + "description": "Group-level milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": 100 + }, + "label_links": [ + { + "id": 11, + "label_id": 2, + "target_id": 1, + "target_type": "Issue", + "created_at": "2017-08-15T18:37:40.795Z", + "updated_at": "2017-08-15T18:37:40.795Z", + "label": { + "id": 2, + "title": "project label", + "color": "#A8D695", + "project_id": null, + "created_at": "2017-08-15T18:37:19.698Z", + "updated_at": "2017-08-15T18:37:19.698Z", + "template": false, + "description": "", + "group_id": 5, + "type": "ProjectLabel", + "priorities": [] + } + } + ] + } + ], + "snippets": [ + + ], + "hooks": [ + + ] +} diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json index 2d8f3d4a566..02450478a77 100644 --- a/spec/lib/gitlab/import_export/project.light.json +++ b/spec/lib/gitlab/import_export/project.light.json @@ -5,9 +5,9 @@ "milestones": [ { "id": 1, - "title": "test milestone", + "title": "Project milestone", "project_id": 8, - "description": "test milestone", + "description": "Project-level milestone", "due_date": null, "created_at": "2016-06-14T15:02:04.415Z", "updated_at": "2016-06-14T15:02:04.415Z", @@ -19,7 +19,7 @@ "labels": [ { "id": 2, - "title": "test2", + "title": "A project label", "color": "#428bca", "project_id": 8, "created_at": "2016-07-22T08:55:44.161Z", @@ -63,28 +63,19 @@ "last_edited_at": null, "last_edited_by_id": null, "group_milestone_id": null, + "milestone": { + "id": 1, + "title": "Project milestone", + "project_id": 8, + "description": "Project-level milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": null + }, "label_links": [ - { - "id": 11, - "label_id": 6, - "target_id": 1, - "target_type": "Issue", - "created_at": "2017-08-15T18:37:40.795Z", - "updated_at": "2017-08-15T18:37:40.795Z", - "label": { - "id": 6, - "title": "group label", - "color": "#A8D695", - "project_id": null, - "created_at": "2017-08-15T18:37:19.698Z", - "updated_at": "2017-08-15T18:37:19.698Z", - "template": false, - "description": "", - "group_id": 5, - "type": "GroupLabel", - "priorities": [] - } - }, { "id": 11, "label_id": 2, @@ -94,14 +85,14 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 6, - "title": "project label", + "title": "Another project label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", "updated_at": "2017-08-15T18:37:19.698Z", "template": false, "description": "", - "group_id": 5, + "group_id": null, "type": "ProjectLabel", "priorities": [] } @@ -109,10 +100,6 @@ ] } ], - "snippets": [ - - ], - "hooks": [ - - ] + "snippets": [], + "hooks": [] } diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index efe11ca794a..4301eee17dc 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -24,7 +24,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do context 'JSON' do it 'restores models based on JSON' do - expect(@restored_project_json).to be true + expect(@restored_project_json).to be_truthy end it 'restore correct project features' do @@ -182,6 +182,53 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end end + shared_examples 'restores project successfully' do + it 'correctly restores project' do + expect(shared.errors).to be_empty + expect(restored_project_json).to be_truthy + end + end + + shared_examples 'restores project correctly' do |**results| + it 'has labels' do + expect(project.labels.size).to eq(results.fetch(:labels, 0)) + end + + it 'has label priorities' do + expect(project.labels.first.priorities).not_to be_empty + end + + it 'has milestones' do + expect(project.milestones.size).to eq(results.fetch(:milestones, 0)) + end + + it 'has issues' do + expect(project.issues.size).to eq(results.fetch(:issues, 0)) + end + + it 'has issue with group label and project label' do + labels = project.issues.first.labels + + expect(labels.where(type: "ProjectLabel").count).to eq(results.fetch(:first_issue_labels, 0)) + end + end + + shared_examples 'restores group correctly' do |**results| + it 'has group label' do + expect(project.group.labels.size).to eq(results.fetch(:labels, 0)) + end + + it 'has group milestone' do + expect(project.group.milestones.size).to eq(results.fetch(:milestones, 0)) + end + + it 'has issue with group label' do + labels = project.issues.first.labels + + expect(labels.where(type: "GroupLabel").count).to eq(results.fetch(:first_issue_labels, 0)) + end + end + context 'Light JSON' do let(:user) { create(:user) } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } @@ -190,33 +237,45 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do let(:restored_project_json) { project_tree_restorer.restore } before do - project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json") - allow(shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') end - context 'project.json file access check' do - it 'does not read a symlink' do - Dir.mktmpdir do |tmpdir| - setup_symlink(tmpdir, 'project.json') - allow(shared).to receive(:export_path).and_call_original + context 'with a simple project' do + before do + project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json") - restored_project_json + restored_project_json + end - expect(shared.errors.first).to be_nil + it_behaves_like 'restores project correctly', + issues: 1, + labels: 1, + milestones: 1, + first_issue_labels: 1 + + context 'project.json file access check' do + it 'does not read a symlink' do + Dir.mktmpdir do |tmpdir| + setup_symlink(tmpdir, 'project.json') + allow(shared).to receive(:export_path).and_call_original + + restored_project_json + + expect(shared.errors).to be_empty + end end end - end - context 'when there is an existing build with build token' do - it 'restores project json correctly' do - create(:ci_build, token: 'abcd') + context 'when there is an existing build with build token' do + before do + create(:ci_build, token: 'abcd') + end - expect(restored_project_json).to be true + it_behaves_like 'restores project successfully' end end - context 'with group' do + context 'with a project that has a group' do let!(:project) do create(:project, :builds_disabled, @@ -227,43 +286,22 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end before do - project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json") + project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.group.json") restored_project_json end - it 'correctly restores project' do - expect(restored_project_json).to be_truthy - expect(shared.errors).to be_empty - end + it_behaves_like 'restores project successfully' + it_behaves_like 'restores project correctly', + issues: 2, + labels: 1, + milestones: 1, + first_issue_labels: 1 - it 'has labels' do - expect(project.labels.count).to eq(2) - end - - it 'creates group label' do - expect(project.group.labels.count).to eq(1) - end - - it 'has label priorities' do - expect(project.labels.first.priorities).not_to be_empty - end - - it 'has milestones' do - expect(project.milestones.count).to eq(1) - end - - it 'has issue' do - expect(project.issues.count).to eq(1) - expect(project.issues.first.labels.count).to eq(2) - end - - it 'has issue with group label and project label' do - labels = project.issues.first.labels - - expect(labels.where(type: "GroupLabel").count).to eq(1) - expect(labels.where(type: "ProjectLabel").count).to eq(1) - end + it_behaves_like 'restores group correctly', + labels: 1, + milestones: 1, + first_issue_labels: 1 end end end diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb index d19bd611919..57b0ef8d1ad 100644 --- a/spec/lib/gitlab/project_template_spec.rb +++ b/spec/lib/gitlab/project_template_spec.rb @@ -4,9 +4,9 @@ describe Gitlab::ProjectTemplate do describe '.all' do it 'returns a all templates' do expected = [ - described_class.new('rails', 'Ruby on Rails'), - described_class.new('spring', 'Spring'), - described_class.new('express', 'NodeJS Express') + described_class.new('rails', 'Ruby on Rails', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/rails'), + described_class.new('spring', 'Spring', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/spring'), + described_class.new('express', 'NodeJS Express', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/express') ] expect(described_class.all).to be_an(Array) @@ -31,7 +31,7 @@ describe Gitlab::ProjectTemplate do end describe 'instance methods' do - subject { described_class.new('phoenix', 'Phoenix Framework') } + subject { described_class.new('phoenix', 'Phoenix Framework', 'Phoenix description', 'link-to-template') } it { is_expected.to respond_to(:logo, :file, :archive_path) } end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 9230d58012f..80bf7986ee0 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -66,12 +66,34 @@ describe Gitlab::Workhorse do let(:diff_refs) { double(base_sha: "base", head_sha: "head") } subject { described_class.send_git_patch(repository, diff_refs) } - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + context 'when Gitaly workhorse_send_git_patch feature is enabled' do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) - expect(key).to eq("Gitlab-Workhorse-Send-Data") - expect(command).to eq("git-format-patch") - expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head") + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("git-format-patch") + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'RawPatchRequest' => Gitaly::RawPatchRequest.new( + repository: repository.gitaly_repository, + left_commit_id: 'base', + right_commit_id: 'head' + ).to_json + }.deep_stringify_keys) + end + end + + context 'when Gitaly workhorse_send_git_patch feature is disabled', :skip_gitaly_mock do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("git-format-patch") + expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head") + end end end @@ -115,14 +137,36 @@ describe Gitlab::Workhorse do describe '.send_git_diff' do let(:diff_refs) { double(base_sha: "base", head_sha: "head") } - subject { described_class.send_git_patch(repository, diff_refs) } + subject { described_class.send_git_diff(repository, diff_refs) } - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + context 'when Gitaly workhorse_send_git_diff feature is enabled' do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) - expect(key).to eq("Gitlab-Workhorse-Send-Data") - expect(command).to eq("git-format-patch") - expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head") + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("git-diff") + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'RawDiffRequest' => Gitaly::RawDiffRequest.new( + repository: repository.gitaly_repository, + left_commit_id: 'base', + right_commit_id: 'head' + ).to_json + }.deep_stringify_keys) + end + end + + context 'when Gitaly workhorse_send_git_diff feature is disabled', :skip_gitaly_mock do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("git-diff") + expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head") + end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a26c71e5155..cf26dbfea49 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -874,7 +874,7 @@ describe Project do let(:project) { create(:project) } context 'when avatar file is uploaded' do - let(:project) { create(:project, :with_avatar) } + let(:project) { create(:project, :public, :with_avatar) } let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" } let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 5d78aed5b4f..f44693a71bb 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1509,7 +1509,9 @@ describe Repository do :gitignore, :koding, :gitlab_ci, - :avatar + :avatar, + :issue_template, + :merge_request_template ]) repository.after_change_head diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 862920ad7c3..9f3b5a809d7 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -222,13 +222,6 @@ describe API::Helpers do expect { current_user }.to raise_error /401/ end - it "returns a 401 response for a token without the appropriate scope" do - personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) - env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token - - expect { current_user }.to raise_error /401/ - end - it "leaves user as is when sudo not specified" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token expect(current_user).to eq(user) @@ -238,18 +231,25 @@ describe API::Helpers do expect(current_user).to eq(user) end + it "does not allow tokens without the appropriate scope" do + personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token + + expect { current_user }.to raise_error API::APIGuard::InsufficientScopeError + end + it 'does not allow revoked tokens' do personal_access_token.revoke! env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token - expect { current_user }.to raise_error /401/ + expect { current_user }.to raise_error API::APIGuard::RevokedError end it 'does not allow expired tokens' do personal_access_token.update_attributes!(expires_at: 1.day.ago) env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token - expect { current_user }.to raise_error /401/ + expect { current_user }.to raise_error API::APIGuard::ExpiredError end end diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb index 388b086ce6a..b1dfcf1b048 100644 --- a/spec/rubocop/cop/migration/datetime_spec.rb +++ b/spec/rubocop/cop/migration/datetime_spec.rb @@ -9,6 +9,7 @@ describe RuboCop::Cop::Migration::Datetime do include CopHelper subject(:cop) { described_class.new } + let(:migration_with_datetime) do %q( class Users < ActiveRecord::Migration @@ -22,6 +23,19 @@ describe RuboCop::Cop::Migration::Datetime do ) end + let(:migration_with_timestamp) do + %q( + class Users < ActiveRecord::Migration + DOWNTIME = false + + def change + add_column(:users, :username, :text) + add_column(:users, :last_sign_in, :timestamp) + end + end + ) + end + let(:migration_without_datetime) do %q( class Users < ActiveRecord::Migration @@ -58,6 +72,17 @@ describe RuboCop::Cop::Migration::Datetime do aggregate_failures do expect(cop.offenses.size).to eq(1) expect(cop.offenses.map(&:line)).to eq([7]) + expect(cop.offenses.first.message).to include('datetime') + end + end + + it 'registers an offense when the ":timestamp" data type is used' do + inspect_source(cop, migration_with_timestamp) + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([7]) + expect(cop.offenses.first.message).to include('timestamp') end end @@ -81,6 +106,7 @@ describe RuboCop::Cop::Migration::Datetime do context 'outside of migration' do it 'registers no offense' do inspect_source(cop, migration_with_datetime) + inspect_source(cop, migration_with_timestamp) inspect_source(cop, migration_without_datetime) inspect_source(cop, migration_with_datetime_with_timezone) diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_entity_spec.rb index 4aeb593da44..87832b3dca1 100644 --- a/spec/serializers/merge_request_entity_spec.rb +++ b/spec/serializers/merge_request_entity_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe MergeRequestEntity do - let(:project) { create :project } + let(:project) { create :project, :repository } let(:resource) { create(:merge_request, source_project: project, target_project: project) } let(:user) { create(:user) } diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb index 9c9b0c4c4a1..a1f7dc44d31 100644 --- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb +++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb @@ -6,11 +6,7 @@ describe MergeRequests::Conflicts::ResolveService do let(:project) { create(:project, :public, :repository) } let(:forked_project) do - forked_project = fork_project(project, user) - TestEnv.copy_repo(forked_project, - bare_repo: TestEnv.forked_repo_path_bare, - refs: TestEnv::FORKED_BRANCH_SHA) - forked_project + fork_project_with_submodules(project, user) end let(:merge_request) do diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 80213d093f1..d1043f99b5a 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -185,7 +185,7 @@ describe MergeRequests::MergeService do context 'source branch removal' do context 'when the source branch is protected' do let(:service) do - described_class.new(project, user, should_remove_source_branch: '1') + described_class.new(project, user, 'should_remove_source_branch' => true) end before do @@ -200,7 +200,7 @@ describe MergeRequests::MergeService do context 'when the source branch is the default branch' do let(:service) do - described_class.new(project, user, should_remove_source_branch: '1') + described_class.new(project, user, 'should_remove_source_branch' => true) end before do @@ -215,10 +215,10 @@ describe MergeRequests::MergeService do context 'when the source branch can be removed' do context 'when MR author set the source branch to be removed' do - let(:service) do - merge_request.merge_params['force_remove_source_branch'] = '1' - merge_request.save! - described_class.new(project, user, commit_message: 'Awesome message') + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } + + before do + merge_request.update_attribute(:merge_params, { 'force_remove_source_branch' => '1' }) end it 'removes the source branch using the author user' do @@ -227,11 +227,20 @@ describe MergeRequests::MergeService do .and_call_original service.execute(merge_request) end + + context 'when the merger set the source branch not to be removed' do + let(:service) { described_class.new(project, user, commit_message: 'Awesome message', 'should_remove_source_branch' => false) } + + it 'does not delete the source branch' do + expect(DeleteBranchService).not_to receive(:new) + service.execute(merge_request) + end + end end context 'when MR merger set the source branch to be removed' do let(:service) do - described_class.new(project, user, commit_message: 'Awesome message', should_remove_source_branch: '1') + described_class.new(project, user, commit_message: 'Awesome message', 'should_remove_source_branch' => true) end it 'removes the source branch using the current user' do diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 7257c359a7e..98409be4236 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -127,10 +127,10 @@ describe MergeRequests::UpdateService, :mailer do end it 'creates system note about discussion lock' do - note = find_note('locked this issue') + note = find_note('locked this merge request') expect(note).not_to be_nil - expect(note.note).to eq 'locked this issue' + expect(note.note).to eq 'locked this merge request' end context 'when not including source branch removal options' do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index b64ca5be8fc..b13e12e7c94 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -731,6 +731,18 @@ describe NotificationService, :mailer do should_not_email(@u_participating) end + it "doesn't send multiple email when a user is subscribed to multiple given labels" do + subscriber_to_both = create(:user) do |user| + [label_1, label_2].each { |label| label.toggle_subscription(user, project) } + end + + notification.relabeled_issue(issue, [label_1, label_2], @u_disabled) + + should_email(subscriber_to_label_1) + should_email(subscriber_to_label_2) + should_email(subscriber_to_both) + end + context 'confidential issues' do let(:author) { create(:user) } let(:assignee) { create(:user) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index b1241cd8d0b..cd473c1f388 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -1145,4 +1145,42 @@ describe SystemNoteService do it { expect(subject.note).to eq "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue" } end end + + describe '.discussion_lock' do + subject { described_class.discussion_lock(noteable, author) } + + context 'discussion unlocked' do + it_behaves_like 'a system note' do + let(:action) { 'unlocked' } + end + + it 'creates the note text correctly' do + [:issue, :merge_request].each do |type| + issuable = create(type) + + expect(described_class.discussion_lock(issuable, author).note) + .to eq("unlocked this #{type.to_s.titleize.downcase}") + end + end + end + + context 'discussion locked' do + before do + noteable.update_attribute(:discussion_locked, true) + end + + it_behaves_like 'a system note' do + let(:action) { 'locked' } + end + + it 'creates the note text correctly' do + [:issue, :merge_request].each do |type| + issuable = create(type, discussion_locked: true) + + expect(described_class.discussion_lock(issuable, author).note) + .to eq("locked this #{type.to_s.titleize.downcase}") + end + end + end + end end diff --git a/spec/support/api/scopes/read_user_shared_examples.rb b/spec/support/api/scopes/read_user_shared_examples.rb index 57e28e040d7..111534f2f26 100644 --- a/spec/support/api/scopes/read_user_shared_examples.rb +++ b/spec/support/api/scopes/read_user_shared_examples.rb @@ -27,10 +27,10 @@ shared_examples_for 'allows the "read_user" scope' do stub_container_registry_config(enabled: true) end - it 'returns a "401" response' do + it 'returns a "403" response' do get api_call.call(path, user, personal_access_token: token) - expect(response).to have_http_status(401) + expect(response).to have_http_status(403) end end end @@ -74,10 +74,10 @@ shared_examples_for 'does not allow the "read_user" scope' do context 'when the requesting token has the "read_user" scope' do let(:token) { create(:personal_access_token, scopes: ['read_user'], user: user) } - it 'returns a "401" response' do + it 'returns a "403" response' do post api_call.call(path, user, personal_access_token: token), attributes_for(:user, projects_limit: 3) - expect(response).to have_http_status(401) + expect(response).to have_http_status(403) end end end diff --git a/spec/support/email_helpers.rb b/spec/support/email_helpers.rb index 3e979f2f470..b39052923dd 100644 --- a/spec/support/email_helpers.rb +++ b/spec/support/email_helpers.rb @@ -1,6 +1,6 @@ module EmailHelpers - def sent_to_user?(user, recipients = email_recipients) - recipients.include?(user.notification_email) + def sent_to_user(user, recipients: email_recipients) + recipients.count { |to| to == user.notification_email } end def reset_delivered_emails! @@ -10,17 +10,17 @@ module EmailHelpers def should_only_email(*users, kind: :to) recipients = email_recipients(kind: kind) - users.each { |user| should_email(user, recipients) } + users.each { |user| should_email(user, recipients: recipients) } expect(recipients.count).to eq(users.count) end - def should_email(user, recipients = email_recipients) - expect(sent_to_user?(user, recipients)).to be_truthy + def should_email(user, times: 1, recipients: email_recipients) + expect(sent_to_user(user, recipients: recipients)).to eq(times) end - def should_not_email(user, recipients = email_recipients) - expect(sent_to_user?(user, recipients)).to be_falsey + def should_not_email(user, recipients: email_recipients) + should_email(user, times: 0, recipients: recipients) end def should_not_email_anyone diff --git a/spec/support/project_forks_helper.rb b/spec/support/project_forks_helper.rb index 0d1c6792d13..d6680735aa1 100644 --- a/spec/support/project_forks_helper.rb +++ b/spec/support/project_forks_helper.rb @@ -52,7 +52,7 @@ module ProjectForksHelper TestEnv.copy_repo(forked_project, bare_repo: TestEnv.forked_repo_path_bare, refs: TestEnv::FORKED_BRANCH_SHA) - + forked_project.repository.after_import forked_project end end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index d9e9409840f..e881ec37ae5 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -12,6 +12,28 @@ describe RepositoryForkWorker do end describe "#perform" do + describe 'when a worker was reset without cleanup' do + let(:jid) { '12345678' } + let(:started_project) { create(:project, :repository, :import_started) } + + it 'creates a new repository from a fork' do + allow(subject).to receive(:jid).and_return(jid) + + expect(shell).to receive(:fork_repository).with( + '/test/path', + project.full_path, + project.repository_storage_path, + fork_project.namespace.full_path + ).and_return(true) + + subject.perform( + project.id, + '/test/path', + project.full_path, + fork_project.namespace.full_path) + end + end + it "creates a new repository from a fork" do expect(shell).to receive(:fork_repository).with( '/test/path', diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb index 100dfc32bbe..5cff5108477 100644 --- a/spec/workers/repository_import_worker_spec.rb +++ b/spec/workers/repository_import_worker_spec.rb @@ -6,6 +6,23 @@ describe RepositoryImportWorker do subject { described_class.new } describe '#perform' do + context 'when worker was reset without cleanup' do + let(:jid) { '12345678' } + let(:started_project) { create(:project, :import_started, import_jid: jid) } + + it 'imports the project successfully' do + allow(subject).to receive(:jid).and_return(jid) + + expect_any_instance_of(Projects::ImportService).to receive(:execute) + .and_return({ status: :ok }) + + expect_any_instance_of(Repository).to receive(:expire_emptiness_caches) + expect_any_instance_of(Project).to receive(:import_finish) + + subject.perform(project.id) + end + end + context 'when the import was successful' do it 'imports a project' do expect_any_instance_of(Projects::ImportService).to receive(:execute) diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore index 520a86352f7..c79ba5080a3 100644 --- a/vendor/gitignore/Android.gitignore +++ b/vendor/gitignore/Android.gitignore @@ -41,7 +41,8 @@ captures/ .idea/libraries # Keystore files -*.jks +# Uncomment the following line if you do not want to check your keystore files in. +#*.jks # External native build folder generated in Android Studio 2.2 and later .externalNativeBuild diff --git a/vendor/gitignore/Autotools.gitignore b/vendor/gitignore/Autotools.gitignore index e3923f96fce..ffa6ecc3f9b 100644 --- a/vendor/gitignore/Autotools.gitignore +++ b/vendor/gitignore/Autotools.gitignore @@ -31,3 +31,12 @@ Makefile.in # http://www.gnu.org/software/texinfo /texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +autom4te.cache diff --git a/vendor/gitignore/Elixir.gitignore b/vendor/gitignore/Elixir.gitignore index ac67aaf3243..b6d65867dac 100644 --- a/vendor/gitignore/Elixir.gitignore +++ b/vendor/gitignore/Elixir.gitignore @@ -1,6 +1,8 @@ /_build /cover /deps +/doc +/.fetch erl_crash.dump *.ez *.beam diff --git a/vendor/gitignore/ExtJs.gitignore b/vendor/gitignore/ExtJs.gitignore index c92aea0fe0c..ab97a8cc3e1 100644 --- a/vendor/gitignore/ExtJs.gitignore +++ b/vendor/gitignore/ExtJs.gitignore @@ -10,3 +10,5 @@ ext/ modern.json modern.jsonp resources/sass/.sass-cache/ +resources/.arch-internal-preview.css +.arch-internal-preview.css diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore index 09dfde64b5f..cca150a88dd 100644 --- a/vendor/gitignore/Global/Matlab.gitignore +++ b/vendor/gitignore/Global/Matlab.gitignore @@ -19,4 +19,4 @@ slprj/ octave-workspace # Simulink autosave extension -.autosave +*.autosave diff --git a/vendor/gitignore/Global/Xcode.gitignore b/vendor/gitignore/Global/Xcode.gitignore index 37de8bb4793..cd0c7d3e45a 100644 --- a/vendor/gitignore/Global/Xcode.gitignore +++ b/vendor/gitignore/Global/Xcode.gitignore @@ -2,11 +2,17 @@ # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ - -## Various settings +*.moved-aside *.pbxuser !default.pbxuser *.mode1v3 @@ -15,9 +21,3 @@ DerivedData/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xccheckout -*.xcscmblueprint diff --git a/vendor/gitignore/Global/macOS.gitignore b/vendor/gitignore/Global/macOS.gitignore index 9d1061e8bc4..135767fc075 100644 --- a/vendor/gitignore/Global/macOS.gitignore +++ b/vendor/gitignore/Global/macOS.gitignore @@ -1,5 +1,5 @@ # General -*.DS_Store +.DS_Store .AppleDouble .LSOverride diff --git a/vendor/gitignore/Joomla.gitignore b/vendor/gitignore/Joomla.gitignore index 53a74e74657..b6bf3a9c96a 100644 --- a/vendor/gitignore/Joomla.gitignore +++ b/vendor/gitignore/Joomla.gitignore @@ -251,7 +251,7 @@ /administrator/language/en-GB/en-GB.tpl_hathor.sys.ini /administrator/language/en-GB/en-GB.xml /administrator/language/overrides/* -/administrator/logs/index.html +/administrator/logs/* /administrator/manifests/* /administrator/modules/mod_custom/* /administrator/modules/mod_feed/* diff --git a/vendor/gitignore/OCaml.gitignore b/vendor/gitignore/OCaml.gitignore index f7817ae5c36..da0b20424a0 100644 --- a/vendor/gitignore/OCaml.gitignore +++ b/vendor/gitignore/OCaml.gitignore @@ -18,3 +18,6 @@ _build/ # oasis generated files setup.data setup.log + +# Merlin configuring file for Vim and Emacs +.merlin diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore index 113294a5f18..af2f537516d 100644 --- a/vendor/gitignore/Python.gitignore +++ b/vendor/gitignore/Python.gitignore @@ -23,6 +23,7 @@ wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -51,6 +52,8 @@ coverage.xml # Django stuff: *.log +.static_storage/ +.media/ local_settings.py # Flask stuff: @@ -84,6 +87,8 @@ celerybeat-schedule env/ venv/ ENV/ +env.bak/ +venv.bak/ # Spyder project settings .spyderproject diff --git a/vendor/gitignore/Qt.gitignore b/vendor/gitignore/Qt.gitignore index fe67fdf1ee6..037a1e75790 100644 --- a/vendor/gitignore/Qt.gitignore +++ b/vendor/gitignore/Qt.gitignore @@ -31,11 +31,9 @@ ui_*.h Makefile* *build-* - # Qt unit tests target_wrapper.* - # QtCreator *.autosave diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore index a0322dbd35a..b6418e51766 100644 --- a/vendor/gitignore/TeX.gitignore +++ b/vendor/gitignore/TeX.gitignore @@ -13,6 +13,7 @@ ## Intermediate documents: *.dvi +*.xdv *-converted-to.* # these rules might exclude image files for figures etc. # *.ps diff --git a/vendor/gitignore/Terraform.gitignore b/vendor/gitignore/Terraform.gitignore index f20453be963..9b5aebb1b35 100644 --- a/vendor/gitignore/Terraform.gitignore +++ b/vendor/gitignore/Terraform.gitignore @@ -5,3 +5,6 @@ # Module directory .terraform/ + +# Variable values for development +terraform.tfvars diff --git a/vendor/gitignore/Umbraco.gitignore b/vendor/gitignore/Umbraco.gitignore index ea05e1fb2a9..b6b0743f62a 100644 --- a/vendor/gitignore/Umbraco.gitignore +++ b/vendor/gitignore/Umbraco.gitignore @@ -1,3 +1,7 @@ +## Ignore Umbraco files/folders generated for each instance +## +## Get latest from https://github.com/github/gitignore/blob/master/Umbraco.gitignore + # Note: VisualStudio gitignore rules may also be relevant # Umbraco diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore index f652b45c2ee..0867ec5a7ee 100644 --- a/vendor/gitignore/VisualStudio.gitignore +++ b/vendor/gitignore/VisualStudio.gitignore @@ -96,6 +96,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -297,3 +300,6 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs + +# OpenCover UI analysis results +OpenCover/ diff --git a/vendor/gitignore/ZendFramework.gitignore b/vendor/gitignore/ZendFramework.gitignore index 80adb154900..f0b7d8585b7 100644 --- a/vendor/gitignore/ZendFramework.gitignore +++ b/vendor/gitignore/ZendFramework.gitignore @@ -19,7 +19,6 @@ temp/ data/DoctrineORMModule/Proxy/ data/DoctrineORMModule/cache/ - # Legacy ZF1 demos/ extras/documentation diff --git a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml b/vendor/gitlab-ci-yml/Go.gitlab-ci.yml index 8a214352d2a..86e4985d8d2 100644 --- a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Go.gitlab-ci.yml @@ -29,7 +29,7 @@ format: compile: stage: build script: - - go build -race -ldflags "-extldflags '-static'" -o mybinary + - go build -race -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/mybinary artifacts: paths: - mybinary diff --git a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml index 91b096654d1..ba2efbd03a0 100644 --- a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml @@ -7,8 +7,8 @@ # This template will build and test your projects as well as create the documentation. # # * Caches downloaded dependencies and plugins between invocation. -# * Does only verify merge requests but deploy built artifacts of the -# master branch. +# * Verify but don't deploy merge requests. +# * Deploy built artifacts from master branch only. # * Shows how to use multiple jobs in test stage for verifying functionality # with multiple JDKs. # * Uses site:stage to collect the documentation for multi-module projects. @@ -20,7 +20,7 @@ variables: MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true" # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used # when running from the command line. - # `installAtEnd` and `deployAtEnd`are only effective with recent version of the corresponding plugins. + # `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins. MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true" # Cache downloaded dependencies and plugins between builds. @@ -100,4 +100,3 @@ pages: - public only: - master - diff --git a/vendor/gitlab-ci-yml/Python.gitlab-ci.yml b/vendor/gitlab-ci-yml/Python.gitlab-ci.yml new file mode 100644 index 00000000000..a2882a5407d --- /dev/null +++ b/vendor/gitlab-ci-yml/Python.gitlab-ci.yml @@ -0,0 +1,32 @@ +# This file is a template, and might need editing before it works on your project. +image: python:latest + +before_script: + - python -V # Print out python version for debugging + +test: + script: + - python setup.py test + - pip install tox flake8 # you can also use tox + - tox -e py36,flake8 + +run: + script: + - python setup.py bdist_wheel + # an alternative approach is to install and run: + - pip install dist/* + # run the command here + artifacts: + paths: + - dist/*.whl + +pages: + script: + - pip install sphinx sphinx-rtd-theme + - cd doc ; make html + - mv build/html/ ../public/ + artifacts: + paths: + - public + only: + - master diff --git a/vendor/licenses.csv b/vendor/licenses.csv index 24623ff4c1f..9f78059986d 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -13,9 +13,9 @@ activemodel,4.2.8,MIT activerecord,4.2.8,MIT activesupport,4.2.8,MIT acts-as-taggable-on,4.0.0,MIT -addressable,2.3.8,Apache 2.0 +addressable,2.5.2,Apache 2.0 after,0.8.2,MIT -ajv,5.2.0,MIT +ajv,5.2.2,MIT ajv-keywords,2.1.0,MIT akismet,2.0.0,MIT align-text,0.1.4,MIT @@ -28,8 +28,6 @@ ansi-regex,2.1.1,MIT ansi-styles,2.2.1,MIT anymatch,1.3.2,ISC append-transform,0.4.0,MIT -aproba,1.1.1,ISC -are-we-there-yet,1.1.4,ISC arel,6.0.4,MIT argparse,1.0.9,MIT arr-diff,2.0.0,MIT @@ -46,21 +44,15 @@ arrify,1.0.1,MIT asana,0.6.0,MIT asciidoctor,1.5.3,MIT asciidoctor-plantuml,0.0.7,MIT -asn1,0.2.3,MIT asn1.js,4.9.1,MIT assert,1.4.1,MIT -assert-plus,0.2.0,MIT async,2.4.1,MIT async-each,1.0.1,MIT -asynckit,0.4.0,MIT atomic,1.1.99,Apache 2.0 attr_encrypted,3.0.3,MIT attr_required,1.0.0,MIT -autoparse,0.3.3,Apache 2.0 autoprefixer,6.7.7,MIT autoprefixer-rails,6.2.3,MIT -aws-sign2,0.6.0,Apache 2.0 -aws4,1.6.0,MIT axiom-types,0.1.1,MIT axios,0.16.2,MIT babel-code-frame,6.22.0,MIT @@ -145,19 +137,16 @@ base64-js,1.2.0,MIT base64id,1.0.0,MIT batch,0.6.1,MIT bcrypt,3.1.11,MIT -bcrypt-pbkdf,1.0.1,New BSD bcrypt_pbkdf,1.0.0,MIT better-assert,1.0.2,MIT big.js,3.1.3,MIT binary-extensions,1.10.0,MIT -bindata,2.3.5,ruby +bindata,2.4.1,ruby blob,0.0.4,unknown -block-stream,0.0.9,ISC bluebird,2.11.0,MIT bn.js,4.11.6,MIT body-parser,1.17.2,MIT bonjour,3.5.0,MIT -boom,2.10.1,New BSD bootstrap-sass,3.3.6,MIT bootstrap_form,2.7.0,MIT brace-expansion,1.1.8,MIT @@ -187,7 +176,6 @@ camelcase-keys,2.1.0,MIT caniuse-api,1.6.1,MIT caniuse-db,1.0.30000649,CC-BY-4.0 carrierwave,1.1.0,MIT -caseless,0.12.0,Apache 2.0 cause,0.1,MIT center-align,0.1.3,MIT chalk,1.1.3,MIT @@ -216,7 +204,6 @@ color-string,0.3.0,MIT colormin,1.1.2,MIT colors,1.1.2,MIT combine-lists,1.0.1,MIT -combined-stream,1.0.5,MIT commander,2.9.0,MIT commondir,1.0.1,MIT component-bind,1.0.0,unknown @@ -233,8 +220,7 @@ configstore,1.4.0,Simplified BSD connect,3.6.3,MIT connect-history-api-fallback,1.3.0,MIT connection_pool,2.2.1,MIT -console-browserify,1.1.0,MIT -console-control-strings,1.1.0,ISC +console-browserify,1.1.0,[Circular] consolidate,0.14.5,MIT constants-browserify,1.0.0,MIT contains-path,0.1.0,MIT @@ -254,7 +240,6 @@ create-hmac,1.1.4,MIT creole,0.5.0,ruby cropper,2.3.0,MIT cross-spawn,5.1.0,MIT -cryptiles,2.0.5,New BSD crypto-browserify,3.11.0,MIT css-color-names,0.0.4,MIT css-loader,0.28.0,MIT @@ -268,13 +253,14 @@ custom-event,1.0.1,MIT d,1.0.0,MIT d3,3.5.11,New BSD d3_rails,3.5.11,MIT -dashdash,1.14.1,MIT date-now,0.1.4,MIT de-indent,1.0.2,MIT debug,2.6.8,MIT debugger-ruby_core_source,1.3.8,MIT decamelize,1.2.0,MIT deckar01-task_list,2.0.0,MIT +declarative,0.0.10,MIT +declarative-option,0.1.0,MIT decompress-response,3.3.0,MIT deep-equal,1.0.1,MIT deep-extend,0.4.2,MIT @@ -283,9 +269,7 @@ default-require-extensions,1.0.0,MIT default_value_for,3.0.2,MIT defined,1.0.0,MIT del,2.2.2,MIT -delayed-stream,1.0.0,MIT delegate,3.1.2,MIT -delegates,1.0.0,MIT depd,1.1.1,MIT des.js,1.0.0,MIT descendants_tracker,0.0.4,MIT @@ -310,14 +294,13 @@ domain_name,0.5.20161021,"Simplified BSD,New BSD,Mozilla Public License 2.0" domelementtype,1.3.0,unknown domhandler,2.3.0,unknown domutils,1.5.1,unknown -doorkeeper,4.2.0,MIT -doorkeeper-openid_connect,1.1.2,MIT +doorkeeper,4.2.6,MIT +doorkeeper-openid_connect,1.2.0,MIT dropzone,4.2.0,MIT dropzonejs-rails,0.7.2,MIT -duplexer,0.1.1,MIT +duplexer,0.1.1,[Circular] duplexer3,0.1.4,New BSD duplexify,3.5.1,MIT -ecc-jsbn,0.1.1,MIT editorconfig,0.13.2,MIT ee-first,1.1.1,MIT ejs,2.5.6,Apache 2.0 @@ -362,7 +345,7 @@ eslint-plugin-import,2.2.0,MIT eslint-plugin-jasmine,2.2.0,MIT eslint-plugin-promise,3.5.0,ISC espree,3.5.0,Simplified BSD -esprima,4.0.0,Simplified BSD +esprima,2.7.3,Simplified BSD esquery,1.0.0,BSD esrecurse,4.1.0,Simplified BSD estraverse,4.1.1,Simplified BSD @@ -388,12 +371,10 @@ express,4.15.4,MIT expression_parser,0.9.0,MIT extend,3.0.1,MIT extglob,0.3.2,MIT -extlib,0.9.16,MIT -extsprintf,1.0.2,MIT -faraday,0.12.1,MIT +faraday,0.12.2,MIT faraday_middleware,0.11.0.1,MIT faraday_middleware-multi_json,0.0.6,MIT -fast-deep-equal,0.1.0,MIT +fast-deep-equal,1.0.0,MIT fast-levenshtein,2.0.6,MIT fast_gettext,1.4.0,"MIT,ruby" fastparse,1.1.1,MIT @@ -428,8 +409,6 @@ follow-redirects,1.2.3,MIT font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License" for-in,0.1.6,MIT for-own,0.1.4,MIT -forever-agent,0.6.1,Apache 2.0 -form-data,2.1.4,MIT formatador,0.2.5,MIT forwarded,0.1.0,MIT fresh,0.5.0,MIT @@ -437,11 +416,8 @@ from,0.1.7,MIT fs-access,1.0.1,MIT fs-extra,0.26.7,MIT fs.realpath,1.0.0,ISC -fsevents,1.1.2,MIT -fstream,1.0.11,ISC -fstream-ignore,1.0.5,ISC +fsevents,,unknown function-bind,1.1.0,MIT -gauge,2.7.4,ISC gemnasium-gitlab-service,0.2.6,MIT gemojione,3.3.0,MIT generate-function,2.0.0,MIT @@ -450,15 +426,15 @@ get-caller-file,1.0.2,ISC get-stdin,4.0.1,MIT get-stream,3.0.0,MIT get_process_mem,0.2.0,MIT -getpass,0.1.7,MIT gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails_js,1.2.0,MIT -gitaly-proto,0.33.0,MIT +gitaly-proto,0.41.0,MIT github-linguist,4.7.6,MIT github-markup,1.6.1,MIT gitlab-flowdock-git-hook,1.0.1,MIT -gitlab-grit,2.8.1,MIT -gitlab-markup,1.5.1,MIT +gitlab-grit,2.8.2,MIT +gitlab-markup,1.6.2,MIT +gitlab-svgs,1.0.4,unknown gitlab_omniauth-ldap,2.0.4,MIT glob,6.0.4,ISC glob-base,0.3.0,MIT @@ -471,9 +447,9 @@ gollum-lib,4.2.7,MIT gollum-rugged_adapter,0.4.4,MIT gon,6.1.0,MIT good-listener,1.2.2,MIT -google-api-client,0.8.7,Apache 2.0 +google-api-client,0.13.6,Apache 2.0 google-protobuf,3.4.0.2,New BSD -googleauth,0.5.1,Apache 2.0 +googleauth,0.5.3,Apache 2.0 got,7.1.0,MIT gpgme,2.0.13,LGPL-2.1+ graceful-fs,4.1.11,ISC @@ -481,14 +457,12 @@ graceful-readlink,1.0.1,MIT grape,1.0.0,MIT grape-entity,0.6.0,MIT grape-route-helpers,2.1.0,MIT -grape_logging,1.6.0,MIT -grpc,1.4.5,New BSD +grape_logging,1.7.0,MIT +grpc,1.6.0,Apache 2.0 gzip-size,3.0.0,MIT hamlit,2.6.1,MIT handle-thing,1.2.5,MIT handlebars,4.0.6,MIT -har-schema,1.0.5,ISC -har-validator,4.2.1,ISC has,1.0.1,MIT has-ansi,2.0.0,MIT has-binary,0.1.7,MIT @@ -496,16 +470,13 @@ has-cors,1.1.0,MIT has-flag,2.0.0,MIT has-symbol-support-x,1.3.0,MIT has-to-string-tag-x,1.3.0,MIT -has-unicode,2.0.1,ISC hash-sum,1.0.2,MIT hash.js,1.0.3,MIT hashie,3.5.6,MIT hashie-forbidden_attributes,0.1.1,MIT -hawk,3.1.3,New BSD he,1.1.1,MIT health_check,2.6.0,MIT hipchat,1.5.2,MIT -hoek,2.16.3,New BSD home-or-tmp,2.0.0,MIT hosted-git-info,2.2.0,ISC hpack.js,2.1.6,MIT @@ -522,7 +493,6 @@ http-errors,1.6.2,MIT http-form_data,1.0.1,MIT http-proxy,1.16.2,MIT http-proxy-middleware,0.17.4,MIT -http-signature,1.1.1,MIT http_parser.rb,0.6.0,MIT httparty,0.13.7,MIT httpclient,2.8.2,ruby @@ -583,15 +553,13 @@ is-resolvable,1.0.0,MIT is-retry-allowed,1.1.0,MIT is-stream,1.1.0,MIT is-svg,2.1.0,MIT -is-typedarray,1.0.0,MIT is-unc-path,0.1.2,MIT is-utf8,0.2.1,MIT is-windows,0.2.0,MIT isarray,1.0.0,MIT isbinaryfile,3.0.2,MIT -isexe,1.1.2,ISC +isexe,2.0.0,ISC isobject,2.1.0,MIT -isstream,0.1.2,MIT istanbul,0.4.5,New BSD istanbul-api,1.1.1,New BSD istanbul-lib-coverage,1.0.1,New BSD @@ -605,7 +573,6 @@ jasmine-core,2.6.3,MIT jasmine-jquery,2.1.1,MIT jed,1.1.1,MIT jira-ruby,1.4.1,MIT -jodid25519,1.0.2,MIT jquery,2.2.1,MIT jquery-atwho-rails,1.3.2,MIT jquery-rails,4.1.1,MIT @@ -615,21 +582,18 @@ js-beautify,1.6.12,MIT js-cookie,2.1.3,MIT js-tokens,3.0.1,MIT js-yaml,3.7.0,MIT -jsbn,0.1.1,MIT jsesc,1.3.0,MIT json,1.8.6,ruby -json-jwt,1.7.1,MIT +json-jwt,1.7.2,MIT json-loader,0.5.7,MIT -json-schema,0.2.3,"AFLv2.1,BSD" json-schema-traverse,0.3.1,MIT json-stable-stringify,1.0.1,MIT json-stringify-safe,5.0.1,ISC -json3,3.3.2,MIT +json3,3.3.2,[Circular] json5,0.5.1,MIT jsonfile,2.4.0,MIT jsonify,0.0.0,Public Domain jsonpointer,4.0.1,MIT -jsprim,1.4.0,MIT jszip,3.1.3,(MIT OR GPL-3.0) jszip-utils,0.0.2,MIT or GPLv3 jwt,1.5.6,MIT @@ -649,7 +613,6 @@ kind-of,3.1.0,MIT klaw,1.3.1,MIT kubeclient,2.2.0,MIT latest-version,1.0.1,MIT -launchy,2.4.3,ISC lazy-cache,1.0.4,MIT lcid,1.0.0,MIT levn,0.3.0,MIT @@ -706,7 +669,7 @@ marked,0.3.6,MIT math-expression-evaluator,1.2.16,MIT media-typer,0.3.0,MIT mem,1.1.0,MIT -memoist,0.15.0,MIT +memoist,0.16.0,MIT memory-fs,0.4.1,MIT meow,3.7.0,MIT merge-descriptors,1.0.1,MIT @@ -714,13 +677,14 @@ method_source,0.8.2,MIT methods,1.1.2,MIT micromatch,2.3.11,MIT miller-rabin,4.0.0,MIT -mime,1.3.4,MIT -mime-db,1.27.0,MIT -mime-types,2.99.3,"MIT,Artistic-2.0,GPL-2.0" +mime,1.3.4,[Circular] +mime-db,1.29.0,MIT +mime-types,3.1,MIT +mime-types-data,3.2016.0521,MIT mimemagic,0.3.0,MIT mimic-fn,1.1.0,MIT mimic-response,1.0.0,MIT -mini_portile2,2.2.0,MIT +mini_portile2,2.3.0,MIT minimalistic-assert,1.0.0,ISC minimatch,3.0.3,ISC minimist,0.0.8,MIT @@ -731,7 +695,7 @@ monaco-editor,0.8.3,MIT mousetrap,1.4.6,Apache 2.0 mousetrap-rails,1.4.6,"MIT,Apache" ms,2.0.0,MIT -multi_json,1.12.1,MIT +multi_json,1.12.2,MIT multi_xml,0.6.0,MIT multicast-dns,6.1.1,MIT multicast-dns-service-types,1.1.0,MIT @@ -739,9 +703,7 @@ multipart-post,2.0.0,MIT mustermann,1.0.0,MIT mustermann-grape,1.0.0,MIT mute-stream,0.0.5,ISC -mysql2,0.4.5,MIT name-all-modules-plugin,1.0.1,MIT -nan,2.6.2,MIT natural-compare,1.4.0,MIT negotiator,0.6.1,MIT nested-error-stacks,1.0.2,MIT @@ -751,22 +713,19 @@ netrc,0.11.0,MIT node-dir,0.1.17,MIT node-forge,0.6.33,BSD node-libs-browser,2.0.0,MIT -node-pre-gyp,0.6.36,New BSD nodemon,1.11.0,MIT -nokogiri,1.8.0,MIT +nokogiri,1.8.1,MIT nopt,3.0.6,ISC -normalize-package-data,2.4.0,Simplified BSD +normalize-package-data,2.3.5,Simplified BSD normalize-path,2.1.1,MIT normalize-range,0.1.2,MIT normalize-url,1.9.1,MIT npm-run-path,2.0.2,MIT -npmlog,4.1.0,ISC null-check,1.0.0,MIT num2fraction,1.2.2,MIT number-is-nan,1.0.1,MIT numerizer,0.1.1,MIT oauth,0.5.1,MIT -oauth-sign,0.8.2,Apache 2.0 oauth2,1.4.0,MIT object-assign,4.1.1,MIT object-component,0.0.3,unknown @@ -777,7 +736,7 @@ oj,2.17.5,MIT omniauth,1.4.2,MIT omniauth-auth0,1.4.1,MIT omniauth-authentiq,0.3.1,MIT -omniauth-azure-oauth2,0.0.6,MIT +omniauth-azure-oauth2,0.0.9,MIT omniauth-cas3,1.1.4,MIT omniauth-facebook,4.0.0,MIT omniauth-github,1.1.2,MIT @@ -786,7 +745,7 @@ omniauth-google-oauth2,0.5.2,MIT omniauth-kerberos,0.3.0,MIT omniauth-multipassword,0.4.2,MIT omniauth-oauth,1.1.0,MIT -omniauth-oauth2,1.3.1,MIT +omniauth-oauth2,1.4.0,MIT omniauth-oauth2-generic,0.2.2,MIT omniauth-saml,1.7.0,MIT omniauth-shibboleth,1.2.1,MIT @@ -839,13 +798,11 @@ pbkdf2,3.0.9,MIT peek,1.0.1,MIT peek-gc,0.0.2,MIT peek-host,1.0.0,MIT -peek-mysql2,1.1.0,MIT peek-performance_bar,1.3.0,MIT peek-pg,1.3.0,MIT peek-rblineprof,0.2.0,MIT peek-redis,1.2.0,MIT peek-sidekiq,1.0.3,MIT -performance-now,0.2.0,MIT pg,0.18.4,"BSD,ruby,GPL" pify,2.3.0,MIT pikaday,1.5.1,"BSD,MIT" @@ -910,6 +867,7 @@ prr,0.0.0,MIT ps-tree,1.1.0,MIT pseudomap,1.0.2,ISC public-encrypt,4.0.0,MIT +public_suffix,3.0.0,MIT punycode,1.4.1,MIT pyu-ruby-sasl,0.0.3.3,MIT q,1.5.0,MIT @@ -917,7 +875,7 @@ qjobs,1.1.5,MIT qs,6.5.0,New BSD query-string,4.3.2,MIT querystring,0.2.0,MIT -querystring-es3,0.2.1,MIT +querystring-es3,0.2.1,[Circular] querystringify,0.0.4,MIT rack,1.6.8,MIT rack-accept,0.4.5,MIT @@ -935,7 +893,7 @@ rails-i18n,4.0.9,MIT railties,4.2.8,MIT rainbow,2.2.2,MIT raindrops,0.18.0,LGPL-2.1+ -rake,12.0.0,MIT +rake,12.1.0,MIT randomatic,1.1.6,MIT randombytes,2.0.3,MIT range-parser,1.2.0,MIT @@ -982,7 +940,7 @@ remove-trailing-separator,1.1.0,ISC repeat-element,1.1.2,MIT repeat-string,1.6.1,MIT repeating,2.0.1,MIT -request,2.81.0,Apache 2.0 +representable,3.0.4,MIT request_store,1.3.1,MIT require-directory,2.1.1,MIT require-from-string,1.2.1,MIT @@ -994,7 +952,7 @@ resolve-from,1.0.1,MIT responders,2.3.0,MIT rest-client,2.0.0,MIT restore-cursor,1.0.1,MIT -retriable,1.4.1,MIT +retriable,3.1.1,MIT right-align,0.1.3,MIT rimraf,2.6.1,ISC rinku,2.0.0,ISC @@ -1013,7 +971,7 @@ rufus-scheduler,3.4.0,MIT rugged,0.26.0,MIT run-async,0.1.0,MIT rx-lite,3.1.2,Apache 2.0 -safe-buffer,5.1.1,MIT +safe-buffer,5.0.1,MIT safe_yaml,1.0.4,MIT sanitize,2.1.0,MIT sass,3.4.22,MIT @@ -1053,7 +1011,6 @@ slack-notifier,1.5.1,MIT slash,1.0.0,MIT slice-ansi,0.0.4,MIT slide,1.1.6,ISC -sntp,1.0.9,BSD socket.io,1.7.3,MIT socket.io-adapter,0.5.0,MIT socket.io-client,1.7.3,MIT @@ -1074,7 +1031,6 @@ sprintf-js,1.0.3,New BSD sprockets,3.7.1,MIT sprockets-rails,3.2.0,MIT sql.js,0.4.0,MIT -sshpk,1.13.0,MIT state_machines,0.4.0,MIT state_machines-activemodel,0.4.0,MIT state_machines-activerecord,0.4.0,MIT @@ -1085,22 +1041,20 @@ stream-http,2.6.3,MIT stream-shift,1.0.0,MIT strict-uri-encode,1.1.0,MIT string-length,1.0.1,MIT -string-width,2.0.0,MIT -string_decoder,1.0.3,MIT +string-width,1.0.2,MIT +string_decoder,0.10.31,MIT stringex,2.7.1,MIT -stringstream,0.0.5,MIT strip-ansi,3.0.1,MIT strip-bom,3.0.0,MIT strip-eof,1.0.0,MIT strip-indent,1.0.1,MIT strip-json-comments,2.0.1,MIT -supports-color,4.2.1,MIT +supports-color,3.2.3,MIT +svg4everybody,2.1.9,CC0-1.0 svgo,0.7.2,MIT sys-filesystem,1.1.6,Artistic 2.0 table,3.8.3,New BSD tapable,0.2.8,MIT -tar,2.2.1,ISC -tar-pack,3.4.0,Simplified BSD temple,0.7.7,MIT test-exclude,4.0.0,ISC text,1.3.1,MIT @@ -1113,6 +1067,7 @@ three-stl-loader,1.0.4,MIT through,2.3.8,MIT thunky,0.1.0,unknown tilt,2.0.6,MIT +time-stamp,2.0.0,MIT timeago.js,2.0.5,MIT timed-out,4.0.1,MIT timers-browserify,2.0.4,MIT @@ -1124,31 +1079,28 @@ to-arraybuffer,1.0.1,MIT to-fast-properties,1.0.2,MIT toml-rb,0.3.15,MIT touch,1.0.0,ISC -tough-cookie,2.3.2,New BSD traverse,0.6.6,MIT trim-newlines,1.0.0,MIT trim-right,1.0.1,MIT truncato,0.7.10,MIT tryit,1.0.3,MIT tty-browserify,0.0.0,MIT -tunnel-agent,0.6.0,Apache 2.0 -tweetnacl,0.14.5,Unlicense type-check,0.3.2,MIT type-is,1.6.15,MIT typedarray,0.0.6,MIT tzinfo,1.2.3,MIT u2f,0.2.1,MIT +uber,0.1.0,MIT uglifier,2.7.2,MIT uglify-js,2.8.29,Simplified BSD uglify-to-browserify,1.0.2,MIT uglifyjs-webpack-plugin,0.4.6,MIT -uid-number,0.0.6,ISC ultron,1.1.0,MIT unc-path-regex,0.1.2,MIT undefsafe,0.0.3,MIT / http://rem.mit-license.org underscore,1.8.3,MIT unf,0.1.4,BSD -unf_ext,0.0.7.2,MIT +unf_ext,0.0.7.4,MIT unicorn,5.1.0,ruby unicorn-worker-killer,0.4.4,ruby uniq,1.0.1,MIT @@ -1166,13 +1118,12 @@ user-home,2.0.0,MIT useragent,2.2.1,MIT util,0.10.3,MIT util-deprecate,1.0.2,MIT -utils-merge,1.0.0,MIT -uuid,3.0.1,MIT +utils-merge,1.0.0,[Circular] +uuid,2.0.3,MIT validate-npm-package-license,3.0.1,Apache 2.0 validates_hostname,1.0.6,MIT vary,1.1.1,MIT vendors,1.0.1,MIT -verror,1.3.6,MIT version_sorter,2.1.0,MIT virtus,1.0.5,MIT visibilityjs,1.2.4,MIT @@ -1200,12 +1151,11 @@ webpack-stats-plugin,0.1.5,MIT websocket-driver,0.6.5,MIT websocket-extensions,0.1.1,MIT whet.extend,0.9.9,MIT -which,1.2.12,ISC +which,1.3.0,ISC which-module,2.0.0,ISC -wide-align,1.1.2,ISC wikicloth,0.8.1,MIT window-size,0.1.0,MIT -wordwrap,0.0.2,MIT/X11 +wordwrap,1.0.0,MIT wrap-ansi,2.1.0,MIT wrappy,1.0.2,ISC write,0.2.1,MIT