diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..7e800609e6c76c14893419965f1ce0ba400d9ace --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +CHANGELOG merge=union \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2c6b65b7b7d7e258056bcfaa17945ac5135a4366..7a7b5c9393626f3d0cbc63b0d25ca84ec7a9feae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,42 @@ +*.log +*.swp +.DS_Store .bundle +.chef +.directory +.envrc +.gitlab_shell_secret +.idea +.rbenv-version .rbx/ -db/*.sqlite3 -db/*.sqlite3-journal -log/*.log* -tmp/ -.sass-cache/ -coverage/* -backups/* -*.swp -public/uploads/ -.ruby-version .ruby-gemset +.ruby-version .rvmrc -.rbenv-version -.directory -nohup.out -Vagrantfile +.sass-cache/ +.secret .vagrant -config/gitlab.yml +Vagrantfile +backups/* +config/aws.yml config/database.yml +config/gitlab.yml config/initializers/omniauth.rb config/initializers/rack_attack.rb config/initializers/smtp_settings.rb -config/unicorn.rb config/resque.yml -config/aws.yml +config/unicorn.rb +coverage/* +db/*.sqlite3 +db/*.sqlite3-journal db/data.yml -.idea -.DS_Store -.chef -vendor/bundle/* -rails_best_practices_output.html doc/code/* -.secret -*.log -public/uploads.* -public/assets/ -.envrc dump.rdb +log/*.log* +nohup.out +public/assets/ +public/uploads.* +public/uploads/ +rails_best_practices_output.html tags -.gitlab_shell_secret +tmp/ +vendor/bundle/* diff --git a/.pkgr.yml b/.pkgr.yml index cf96e7916d8a34eb46a95ae1b909bffc1a01941e..8fc9fddf8f79d5f5dc391732879b307712d201cf 100644 --- a/.pkgr.yml +++ b/.pkgr.yml @@ -1,9 +1,12 @@ user: git group: git +services: + - postgres before_precompile: ./bin/pkgr_before_precompile.sh targets: debian-7: &wheezy build_dependencies: + - libkrb5-dev - libicu-dev - cmake - pkg-config @@ -14,6 +17,7 @@ targets: ubuntu-12.04: *wheezy ubuntu-14.04: build_dependencies: + - libkrb5-dev - libicu-dev - cmake - pkg-config @@ -23,6 +27,7 @@ targets: - git centos-6: build_dependencies: + - krb5-devel - libicu-devel - cmake - pkgconfig diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000000000000000000000000000000000000..7290d627d2434a2cd10a81fce5fdbbf48ddb2114 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,1006 @@ +Style/AccessModifierIndentation: + Description: Check indentation of private/protected visibility modifiers. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' + Enabled: true + +Style/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + Enabled: false + +Style/Alias: + Description: 'Use alias_method instead of alias.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' + Enabled: true + +Style/AlignArray: + Description: >- + Align the elements of an array literal if they span more than + one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' + Enabled: true + +Style/AlignHash: + Description: >- + Align the elements of a hash literal if they span more than + one line. + Enabled: true + +Style/AlignParameters: + Description: >- + Align the parameters of a method call if they span more + than one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' + Enabled: false + +Style/AndOr: + Description: 'Use &&/|| instead of and/or.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' + Enabled: false + +Style/ArrayJoin: + Description: 'Use Array#join instead of Array#*.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' + Enabled: false + +Style/AsciiComments: + Description: 'Use only ascii symbols in comments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' + Enabled: true + +Style/AsciiIdentifiers: + Description: 'Use only ascii symbols in identifiers.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' + Enabled: true + +Style/Attr: + Description: 'Checks for uses of Module#attr.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' + Enabled: false + +Style/BeginBlock: + Description: 'Avoid the use of BEGIN blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' + Enabled: true + +Style/BarePercentLiterals: + Description: 'Checks if usage of %() or %Q() matches configuration.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' + Enabled: false + +Style/BlockComments: + Description: 'Do not use block comments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' + Enabled: false + +Style/BlockEndNewline: + Description: 'Put end statement of multiline block on its own line.' + Enabled: true + +Style/Blocks: + Description: >- + Avoid using {...} for multi-line blocks (multiline chaining is + always ugly). + Prefer {...} over do...end for single-line blocks. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' + Enabled: true + +Style/BracesAroundHashParameters: + Description: 'Enforce braces style around hash parameters.' + Enabled: false + +Style/CaseEquality: + Description: 'Avoid explicit use of the case equality operator(===).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' + Enabled: false + +Style/CaseIndentation: + Description: 'Indentation of when in a case/when/[else/]end.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' + Enabled: true + +Style/CharacterLiteral: + Description: 'Checks for uses of character literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' + Enabled: true + +Style/ClassAndModuleCamelCase: + Description: 'Use CamelCase for classes and modules.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' + Enabled: true + +Style/ClassAndModuleChildren: + Description: 'Checks style of children classes and modules.' + Enabled: false + +Style/ClassCheck: + Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' + Enabled: false + +Style/ClassMethods: + Description: 'Use self when defining module/class methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-singletons' + Enabled: false + +Style/ClassVars: + Description: 'Avoid the use of class variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' + Enabled: true + +Style/ColonMethodCall: + Description: 'Do not use :: for method call.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' + Enabled: false + +Style/CommentAnnotation: + Description: >- + Checks formatting of special comments + (TODO, FIXME, OPTIMIZE, HACK, REVIEW). + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' + Enabled: false + +Style/CommentIndentation: + Description: 'Indentation of comments.' + Enabled: true + +Style/ConstantName: + Description: 'Constants should use SCREAMING_SNAKE_CASE.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' + Enabled: true + +Style/DefWithParentheses: + Description: 'Use def with parentheses when there are arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' + Enabled: false + +Style/DeprecatedHashMethods: + Description: 'Checks for use of deprecated Hash methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' + Enabled: false + +Style/Documentation: + Description: 'Document classes and non-namespace modules.' + Enabled: false + +Style/DotPosition: + Description: 'Checks the position of the dot in multi-line method calls.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' + Enabled: false + +Style/DoubleNegation: + Description: 'Checks for uses of double negation (!!).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' + Enabled: false + +Style/EachWithObject: + Description: 'Prefer `each_with_object` over `inject` or `reduce`.' + Enabled: false + +Style/ElseAlignment: + Description: 'Align elses and elsifs correctly.' + Enabled: true + +Style/EmptyElse: + Description: 'Avoid empty else-clauses.' + Enabled: false + +Style/EmptyLineBetweenDefs: + Description: 'Use empty lines between defs.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' + Enabled: false + +Style/EmptyLines: + Description: "Don't use several empty lines in a row." + Enabled: false + +Style/EmptyLinesAroundAccessModifier: + Description: "Keep blank lines around access modifiers." + Enabled: false + +Style/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around block bodies." + Enabled: false + +Style/EmptyLinesAroundClassBody: + Description: "Keeps track of empty lines around class bodies." + Enabled: false + +Style/EmptyLinesAroundModuleBody: + Description: "Keeps track of empty lines around module bodies." + Enabled: false + +Style/EmptyLinesAroundMethodBody: + Description: "Keeps track of empty lines around method bodies." + Enabled: false + +Style/EmptyLiteral: + Description: 'Prefer literals to Array.new/Hash.new/String.new.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' + Enabled: false + +Style/EndBlock: + Description: 'Avoid the use of END blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' + Enabled: false + +Style/EndOfLine: + Description: 'Use Unix-style line endings.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' + Enabled: false + +Style/EvenOdd: + Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' + Enabled: false + +Style/FileName: + Description: 'Use snake_case for source file names.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' + Enabled: false + +Style/FlipFlop: + Description: 'Checks for flip flops' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' + Enabled: false + +Style/For: + Description: 'Checks use of for or each in multiline loops.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' + Enabled: false + +Style/FormatString: + Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' + Enabled: false + +Style/GlobalVars: + Description: 'Do not introduce global variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' + Enabled: false + +Style/GuardClause: + Description: 'Check for conditionals that can be replaced with guard clauses' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' + Enabled: false + +Style/HashSyntax: + Description: >- + Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax + { :a => 1, :b => 2 }. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' + Enabled: true + +Style/IfUnlessModifier: + Description: >- + Favor modifier if/unless usage when you have a + single-line body. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' + Enabled: false + +Style/IfWithSemicolon: + Description: 'Do not use if x; .... Use the ternary operator instead.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' + Enabled: false + +Style/IndentationConsistency: + Description: 'Keep indentation straight.' + Enabled: true + +Style/IndentationWidth: + Description: 'Use 2 spaces for indentation.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' + Enabled: true + +Style/IndentArray: + Description: >- + Checks the indentation of the first element in an array + literal. + Enabled: false + +Style/IndentHash: + Description: 'Checks the indentation of the first key in a hash literal.' + Enabled: false + +Style/InfiniteLoop: + Description: 'Use Kernel#loop for infinite loops.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' + Enabled: false + +Style/Lambda: + Description: 'Use the new lambda literal syntax for single-line blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' + Enabled: false + +Style/LambdaCall: + Description: 'Use lambda.call(...) instead of lambda.(...).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' + Enabled: false + +Style/LeadingCommentSpace: + Description: 'Comments should start with a space.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' + Enabled: false + +Style/LineEndConcatenation: + Description: >- + Use \ instead of + or << to concatenate two string literals at + line end. + Enabled: false + +Style/MethodCallParentheses: + Description: 'Do not use parentheses for method calls with no arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' + Enabled: false + +Style/MethodDefParentheses: + Description: >- + Checks if the method definitions have or don't have + parentheses. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' + Enabled: false + +Style/MethodName: + Description: 'Use the configured style when naming methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' + Enabled: false + +Style/ModuleFunction: + Description: 'Checks for usage of `extend self` in modules.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' + Enabled: false + +Style/MultilineBlockChain: + Description: 'Avoid multi-line chains of blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' + Enabled: false + +Style/MultilineBlockLayout: + Description: 'Ensures newlines after multiline block do statements.' + Enabled: true + +Style/MultilineIfThen: + Description: 'Do not use then for multi-line if/unless.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' + Enabled: false + +Style/MultilineOperationIndentation: + Description: >- + Checks indentation of binary operations that span more than + one line. + Enabled: false + +Style/MultilineTernaryOperator: + Description: >- + Avoid multi-line ?: (the ternary operator); + use if/unless instead. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' + Enabled: false + +Style/NegatedIf: + Description: >- + Favor unless over if for negative conditions + (or control flow or). + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' + Enabled: false + +Style/NegatedWhile: + Description: 'Favor until over while for negative conditions.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' + Enabled: false + +Style/NestedTernaryOperator: + Description: 'Use one expression per branch in a ternary operator.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' + Enabled: true + +Style/Next: + Description: 'Use `next` to skip iteration instead of a condition at the end.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' + Enabled: false + +Style/NilComparison: + Description: 'Prefer x.nil? to x == nil.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' + Enabled: true + +Style/NonNilCheck: + Description: 'Checks for redundant nil checks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' + Enabled: true + +Style/Not: + Description: 'Use ! instead of not.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' + Enabled: true + +Style/NumericLiterals: + Description: >- + Add underscores to large numeric literals to improve their + readability. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' + Enabled: false + +Style/OneLineConditional: + Description: >- + Favor the ternary operator(?:) over + if/then/else/end constructs. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' + Enabled: true + +Style/OpMethod: + Description: 'When defining binary operators, name the argument other.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' + Enabled: false + +Style/ParenthesesAroundCondition: + Description: >- + Don't use parentheses around the condition of an + if/unless/while. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' + Enabled: true + +Style/PercentLiteralDelimiters: + Description: 'Use `%`-literal delimiters consistently' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' + Enabled: false + +Style/PercentQLiterals: + Description: 'Checks if uses of %Q/%q match the configured preference.' + Enabled: false + +Style/PerlBackrefs: + Description: 'Avoid Perl-style regex back references.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' + Enabled: false + +Style/PredicateName: + Description: 'Check the names of predicate methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' + Enabled: false + +Style/Proc: + Description: 'Use proc instead of Proc.new.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' + Enabled: false + +Style/RaiseArgs: + Description: 'Checks the arguments passed to raise/fail.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' + Enabled: false + +Style/RedundantBegin: + Description: "Don't use begin blocks when they are not needed." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' + Enabled: false + +Style/RedundantException: + Description: "Checks for an obsolete RuntimeException argument in raise/fail." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' + Enabled: false + +Style/RedundantReturn: + Description: "Don't use return where it's not required." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' + Enabled: true + +Style/RedundantSelf: + Description: "Don't use self where it's not needed." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' + Enabled: false + +Style/RegexpLiteral: + Description: >- + Use %r for regular expressions matching more than + `MaxSlashes` '/' characters. + Use %r only for regular expressions matching more than + `MaxSlashes` '/' character. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' + Enabled: false + +Style/RescueModifier: + Description: 'Avoid using rescue in its modifier form.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' + Enabled: false + +Style/SelfAssignment: + Description: >- + Checks for places where self-assignment shorthand should have + been used. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' + Enabled: false + +Style/Semicolon: + Description: "Don't use semicolons to terminate expressions." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' + Enabled: false + +Style/SignalException: + Description: 'Checks for proper usage of fail and raise.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' + Enabled: false + +Style/SingleLineBlockParams: + Description: 'Enforces the names of some block params.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' + Enabled: false + +Style/SingleLineMethods: + Description: 'Avoid single-line methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' + Enabled: false + +Style/SingleSpaceBeforeFirstArg: + Description: >- + Checks that exactly one space is used between a method name + and the first argument for method calls without parentheses. + Enabled: false + +Style/SpaceAfterColon: + Description: 'Use spaces after colons.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceAfterComma: + Description: 'Use spaces after commas.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceAfterControlKeyword: + Description: 'Use spaces after if/elsif/unless/while/until/case/when.' + Enabled: false + +Style/SpaceAfterMethodName: + Description: >- + Do not put a space between a method name and the opening + parenthesis in a method definition. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' + Enabled: false + +Style/SpaceAfterNot: + Description: Tracks redundant space after the ! operator. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' + Enabled: false + +Style/SpaceAfterSemicolon: + Description: 'Use spaces after semicolons.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceBeforeBlockBraces: + Description: >- + Checks that the left block brace has or doesn't have space + before it. + Enabled: false + +Style/SpaceBeforeComma: + Description: 'No spaces before commas.' + Enabled: false + +Style/SpaceBeforeComment: + Description: >- + Checks for missing space between code and a comment on the + same line. + Enabled: false + +Style/SpaceBeforeSemicolon: + Description: 'No spaces before semicolons.' + Enabled: false + +Style/SpaceInsideBlockBraces: + Description: >- + Checks that block braces have or don't have surrounding space. + For blocks taking parameters, checks that the left brace has + or doesn't have trailing space. + Enabled: false + +Style/SpaceAroundEqualsInParameterDefault: + Description: >- + Checks that the equals signs in parameter default assignments + have or don't have surrounding space depending on + configuration. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' + Enabled: false + +Style/SpaceAroundOperators: + Description: 'Use spaces around operators.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceBeforeModifierKeyword: + Description: 'Put a space before the modifier keyword.' + Enabled: false + +Style/SpaceInsideBrackets: + Description: 'No spaces after [ or before ].' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' + Enabled: false + +Style/SpaceInsideHashLiteralBraces: + Description: "Use spaces inside hash literal braces - or don't." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: true + +Style/SpaceInsideParens: + Description: 'No spaces after ( or before ).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' + Enabled: false + +Style/SpaceInsideRangeLiteral: + Description: 'No spaces inside range literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' + Enabled: false + +Style/SpecialGlobalVars: + Description: 'Avoid Perl-style global variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' + Enabled: false + +Style/StringLiterals: + Description: 'Checks if uses of quotes match the configured preference.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' + Enabled: false + +Style/StringLiteralsInInterpolation: + Description: >- + Checks if uses of quotes inside expressions in interpolated + strings match the configured preference. + Enabled: false + +Style/SymbolProc: + Description: 'Use symbols as procs instead of blocks when possible.' + Enabled: false + +Style/Tab: + Description: 'No hard tabs.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' + Enabled: false + +Style/TrailingBlankLines: + Description: 'Checks trailing blank lines and final newline.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' + Enabled: true + +Style/TrailingComma: + Description: 'Checks for trailing comma in parameter lists and literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' + Enabled: false + +Style/TrailingWhitespace: + Description: 'Avoid trailing whitespace.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' + Enabled: false + +Style/TrivialAccessors: + Description: 'Prefer attr_* methods to trivial readers/writers.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' + Enabled: false + +Style/UnlessElse: + Description: >- + Do not use unless with else. Rewrite these with the positive + case first. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' + Enabled: false + +Style/UnneededCapitalW: + Description: 'Checks for %W when interpolation is not needed.' + Enabled: false + +Style/UnneededPercentQ: + Description: 'Checks for %q/%Q when single quotes or double quotes would do.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' + Enabled: false + +Style/UnneededPercentX: + Description: 'Checks for %x when `` would do.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' + Enabled: false + +Style/VariableInterpolation: + Description: >- + Don't interpolate global, instance and class variables + directly in strings. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' + Enabled: false + +Style/VariableName: + Description: 'Use the configured style when naming variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' + Enabled: false + +Style/WhenThen: + Description: 'Use when x then ... for one-line cases.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' + Enabled: false + +Style/WhileUntilDo: + Description: 'Checks for redundant do after while or until.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' + Enabled: false + +Style/WhileUntilModifier: + Description: >- + Favor modifier while/until usage when you have a + single-line body. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' + Enabled: false + +Style/WordArray: + Description: 'Use %w or %W for arrays of words.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' + Enabled: false + +#################### Metrics ################################ + +Metrics/AbcSize: + Description: >- + A calculated magnitude based on number of assignments, + branches, and conditions. + Enabled: false + +Metrics/BlockNesting: + Description: 'Avoid excessive block nesting' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' + Enabled: false + +Metrics/ClassLength: + Description: 'Avoid classes longer than 100 lines of code.' + Enabled: false + +Metrics/CyclomaticComplexity: + Description: >- + A complexity metric that is strongly correlated to the number + of test cases needed to validate a method. + Enabled: false + +Metrics/LineLength: + Description: 'Limit lines to 80 characters.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' + Enabled: false + +Metrics/MethodLength: + Description: 'Avoid methods longer than 10 lines of code.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' + Enabled: false + +Metrics/ParameterLists: + Description: 'Avoid parameter lists longer than three or four parameters.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' + Enabled: false + +Metrics/PerceivedComplexity: + Description: >- + A complexity metric geared towards measuring complexity for a + human reader. + Enabled: false + +#################### Lint ################################ +### Warnings + +Lint/AmbiguousOperator: + Description: >- + Checks for ambiguous operators in the first argument of a + method invocation without parentheses. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' + Enabled: false + +Lint/AmbiguousRegexpLiteral: + Description: >- + Checks for ambiguous regexp literals in the first argument of + a method invocation without parenthesis. + Enabled: false + +Lint/AssignmentInCondition: + Description: "Don't use assignment in conditions." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' + Enabled: false + +Lint/BlockAlignment: + Description: 'Align block ends correctly.' + Enabled: false + +Lint/ConditionPosition: + Description: >- + Checks for condition placed in a confusing position relative to + the keyword. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' + Enabled: false + +Lint/Debugger: + Description: 'Check for debugger calls.' + Enabled: false + +Lint/DefEndAlignment: + Description: 'Align ends corresponding to defs correctly.' + Enabled: false + +Lint/DeprecatedClassMethods: + Description: 'Check for deprecated class method calls.' + Enabled: false + +Lint/ElseLayout: + Description: 'Check for odd code arrangement in an else block.' + Enabled: false + +Lint/EmptyEnsure: + Description: 'Checks for empty ensure block.' + Enabled: false + +Lint/EmptyInterpolation: + Description: 'Checks for empty string interpolation.' + Enabled: false + +Lint/EndAlignment: + Description: 'Align ends correctly.' + Enabled: false + +Lint/EndInMethod: + Description: 'END blocks should not be placed inside method definitions.' + Enabled: false + +Lint/EnsureReturn: + Description: 'Do not use return in an ensure block.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' + Enabled: false + +Lint/Eval: + Description: 'The use of eval represents a serious security risk.' + Enabled: false + +Lint/HandleExceptions: + Description: "Don't suppress exception." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' + Enabled: false + +Lint/InvalidCharacterLiteral: + Description: >- + Checks for invalid character literals with a non-escaped + whitespace character. + Enabled: false + +Lint/LiteralInCondition: + Description: 'Checks of literals used in conditions.' + Enabled: false + +Lint/LiteralInInterpolation: + Description: 'Checks for literals used in interpolation.' + Enabled: false + +Lint/Loop: + Description: >- + Use Kernel#loop with break rather than begin/end/until or + begin/end/while for post-loop tests. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' + Enabled: false + +Lint/ParenthesesAsGroupedExpression: + Description: >- + Checks for method calls with a space before the opening + parenthesis. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' + Enabled: true + +Lint/RequireParentheses: + Description: >- + Use parentheses in the method call to avoid confusion + about precedence. + Enabled: false + +Lint/RescueException: + Description: 'Avoid rescuing the Exception class.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' + Enabled: false + +Lint/ShadowingOuterLocalVariable: + Description: >- + Do not use the same name as outer local variable + for block arguments or block local variables. + Enabled: false + +Lint/SpaceBeforeFirstArg: + Description: >- + Put a space between a method name and the first argument + in a method call without parentheses. + Enabled: false + +Lint/StringConversionInInterpolation: + Description: 'Checks for Object#to_s usage in string interpolation.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' + Enabled: false + +Lint/UnderscorePrefixedVariableName: + Description: 'Do not use prefix `_` for a variable that is used.' + Enabled: true + +Lint/UnusedBlockArgument: + Description: 'Checks for unused block arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' + Enabled: false + +Lint/UnusedMethodArgument: + Description: 'Checks for unused method arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' + Enabled: false + +Lint/UnreachableCode: + Description: 'Unreachable code.' + Enabled: false + +Lint/UselessAccessModifier: + Description: 'Checks for useless access modifiers.' + Enabled: false + +Lint/UselessAssignment: + Description: 'Checks for useless assignment to a local variable.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' + Enabled: false + +Lint/UselessComparison: + Description: 'Checks for comparison of something with itself.' + Enabled: false + +Lint/UselessElseWithoutRescue: + Description: 'Checks for useless `else` in `begin..end` without `rescue`.' + Enabled: false + +Lint/UselessSetterCall: + Description: 'Checks for useless setter call to a local variable.' + Enabled: false + +Lint/Void: + Description: 'Possible use of operator/literal/variable in void context.' + Enabled: false + +##################### Rails ################################## + +Rails/ActionFilter: + Description: 'Enforces consistent use of action filter methods.' + Enabled: false + +Rails/DefaultScope: + Description: 'Checks if the argument passed to default_scope is a block.' + Enabled: false + +Rails/Delegate: + Description: 'Prefer delegate method for delegations.' + Enabled: false + +Rails/HasAndBelongsToMany: + Description: 'Prefer has_many :through to has_and_belongs_to_many.' + Enabled: true + +Rails/Output: + Description: 'Checks for calls to puts, print, etc.' + Enabled: true + +Rails/ReadWriteAttribute: + Description: >- + Checks for read_attribute(:attr) and + write_attribute(:attr, val). + Enabled: false + +Rails/ScopeArgs: + Description: 'Checks the arguments of ActiveRecord scopes.' + Enabled: false + +Rails/Validation: + Description: 'Use validates :attribute, hash of validations.' + Enabled: false + + +# Exclude some of GitLab files +# +# +AllCops: + RunRailsCops: true + Exclude: + - 'spec/**/*' + - 'features/**/*' + - 'vendor/**/*' + - 'db/**/*' + - 'tmp/**/*' + - 'bin/**/*' + - 'lib/backup/**/*' + - 'lib/tasks/**/*' + - 'lib/email_validator.rb' + - 'lib/gitlab/upgrader.rb' + - 'lib/gitlab/seeder.rb' diff --git a/.ruby-version b/.ruby-version index ac2cdeba0137a6c2cbca06867b7ab994a913e294..cd57a8b95d6d1de42ed8ae15fa6c449ed7c9863c 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.1.3 +2.1.5 diff --git a/CHANGELOG b/CHANGELOG index 3cd32a75dde923932d80de0a9d0c6f0a92f55e07..839dd0db10701182cadabb540abfe373617cfcf1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,335 @@ +Please view this file on the master branch, on stable branches it's out of date. + +v 7.10.0 (unreleased) + - Fix broken side-by-side diff view on merge request page (Stan Hu) + - Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu) + - Allow HTML tags in Markdown input + - Fix code unfold not working on Compare commits page (Stan Hu) + - Fix dots in Wiki slugs causing errors (Stan Hu) + - Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg) + - Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu) + - Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu) + - Reduce Rack Attack false positives causing 403 errors during HTTP authentication (Stan Hu) + - enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger) + - extend the commit calendar to show the actual commits made on a date (Hannes Rosenögger) + - Fix a link in the patch update guide + - Add a service to support external wikis (Hannes Rosenögger) + - Omit the "email patches" link and fix plain diff view for merge commits + - List new commits for newly pushed branch in activity view. + - Add sidetiq gem dependency to match EE + - Add changelog, license and contribution guide links to project sidebar. + - Improve diff UI + - Fix alignment of navbar toggle button (Cody Mize) + - Fix checkbox rendering for nested task lists + - Identical look of selectboxes in UI + - Upgrade the gitlab_git gem to version 7.1.3 + - Move "Import existing repository by URL" option to button. + - Improve error message when save profile has error. + - Passing the name of pushed ref to CI service (requires GitLab CI 7.9+) + - Add location field to user profile + - Fix print view for markdown files and wiki pages + - Fix errors when deleting old backups + - Improve GitLab performance when working with git repositories + - Add tag message and last commit to tag hook (Kamil Trzciński) + - Restrict permissions on backup files + - Improve oauth accounts UI in profile page + - Add ability to unlink connected accounts + - Replace commits calendar with faster contribution calendar that includes issues and merge requests + - Add inifinite scroll to user page activity + - Don't include system notes in issue/MR comment count. + - Don't mark merge request as updated when merge status relative to target branch changes. + - Link note avatar to user. + - Make Git-over-SSH errors more descriptive. + - Fix EmailsOnPush. + - Refactor issue filtering + - AJAX selectbox for issue assignee and author filters + - Fix issue with missing options in issue filtering dropdown if selected one + - Prevent holding Control-Enter or Command-Enter from posting comment multiple times. + - Prevent note form from being cleared when submitting failed. + - Improve file icons rendering on tree (Sullivan Sénéchal) + - API: Add pagination to project events + +v 7.9.0 + - Send EmailsOnPush email when branch or tag is created or deleted. + - Faster merge request processing for large repository + - Prevent doubling AJAX request with each commit visit via Turbolink + - Prevent unnecessary doubling of js events on import pages and user calendar + +v 7.9.1 + - Include missing events and fix save functionality in admin service template settings form (Stan Hu) + - Fix "Import projects from" button to show the correct instructions (Stan Hu) + - Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu) + - Fix for LDAP with commas in DN + - Fix missing events and in admin Slack service template settings form (Stan Hu) + - Don't show commit comment button when user is not signed in. + - Downgrade gemnasium-gitlab-service gem + +v 7.9.0 + - Add HipChat integration documentation (Stan Hu) + - Update documentation for object_kind field in Webhook push and tag push Webhooks (Stan Hu) + - Fix broken email images (Hannes Rosenögger) + - Automatically config git if user forgot, where possible (Zeger-Jan van de Weg) + - Fix mass SQL statements on initial push (Hannes Rosenögger) + - Add tag push notifications and normalize HipChat and Slack messages to be consistent (Stan Hu) + - Add comment notification events to HipChat and Slack services (Stan Hu) + - Add issue and merge request events to HipChat and Slack services (Stan Hu) + - Fix merge request URL passed to Webhooks. (Stan Hu) + - Fix bug that caused a server error when editing a comment to "+1" or "-1" (Stan Hu) + - Fix code preview theme setting for comments, issues, merge requests, and snippets (Stan Hu) + - Move labels/milestones tabs to sidebar + - Upgrade Rails gem to version 4.1.9. + - Improve error messages for file edit failures + - Improve UI for commits, issues and merge request lists + - Fix commit comments on first line of diff not rendering in Merge Request Discussion view. + - Allow admins to override restricted project visibility settings. + - Move restricted visibility settings from gitlab.yml into the web UI. + - Improve trigger merge request hook when source project branch has been updated (Kirill Zaitsev) + - Save web edit in new branch + - Fix ordering of imported but unchanged projects (Marco Wessel) + - Mobile UI improvements: make aside content expandable + - Expose avatar_url in projects API + - Fix checkbox alignment on the application settings page. + - Generalize image upload in drag and drop in markdown to all files (Hannes Rosenögger) + - Fix mass-unassignment of issues (Robert Speicher) + - Fix hidden diff comments in merge request discussion view + - Allow user confirmation to be skipped for new users via API + - Add a service to send updates to an Irker gateway (Romain Coltel) + - Add brakeman (security scanner for Ruby on Rails) + - Slack username and channel options + - Add grouped milestones from all projects to dashboard. + - Web hook sends pusher email as well as commiter + - Add Bitbucket omniauth provider. + - Add Bitbucket importer. + - Support referencing issues to a project whose name starts with a digit + - Condense commits already in target branch when updating merge request source branch. + - Send notifications and leave system comments when bulk updating issues. + - Automatically link commit ranges to compare page: sha1...sha4 or sha1..sha4 (includes sha1 in comparison) + - Move groups page from profile to dashboard + - Starred projects page at dashboard + - Blocking user does not remove him/her from project/groups but show blocked label + - Change subject of EmailsOnPush emails to include namespace, project and branch. + - Change subject of EmailsOnPush emails to include first commit message when multiple were pushed. + - Remove confusing footer from EmailsOnPush mail body. + - Add list of changed files to EmailsOnPush emails. + - Add option to send EmailsOnPush emails from committer email if domain matches. + - Add option to disable code diffs in EmailOnPush emails. + - Wrap commit message in EmailsOnPush email. + - Send EmailsOnPush emails when deleting commits using force push. + - Fix EmailsOnPush email comparison link to include first commit. + - Fix highliht of selected lines in file + - Reject access to group/project avatar if the user doesn't have access. + - Add database migration to clean group duplicates with same path and name (Make sure you have a backup before update) + - Add GitLab active users count to rake gitlab:check + - Starred projects page at dashboard + - Make email display name configurable + - Improve json validation in hook data + - Use Emoji One + - Updated emoji help documentation to properly reference EmojiOne. + - Fix missing GitHub organisation repositories on import page. + - Added blue theme + - Remove annoying notice messages when create/update merge request + - Allow smb:// links in Markdown text. + - Filter merge request by title or description at Merge Requests page + - Block user if he/she was blocked in Active Directory + - Fix import pages not working after first load. + - Use custom LDAP label in LDAP signin form. + - Execute hooks and services when branch or tag is created or deleted through web interface. + - Block and unblock user if he/she was blocked/unblocked in Active Directory + - Raise recommended number of unicorn workers from 2 to 3 + - Use same layout and interactivity for project members as group members. + - Prevent gitlab-shell character encoding issues by receiving its changes as raw data. + - Ability to unsubscribe/subscribe to issue or merge request + - Delete deploy key when last connection to a project is destroyed. + - Fix invalid Atom feeds when using emoji, horizontal rules, or images (Christian Walther) + - Backup of repositories with tar instead of git bundle (only now are git-annex files included in the backup) + - Add canceled status for CI + +v 7.8.4 + - Fix issue_tracker_id substitution in custom issue trackers + - Fix path and name duplication in namespaces + +v 7.8.3 + - Bump version of gitlab_git fixing annotated tags without message + +v 7.8.2 + - Fix service migration issue when upgrading from versions prior to 7.3 + - Fix setting of the default use project limit via admin UI + - Fix showing of already imported projects for GitLab and Gitorious importers + - Fix response of push to repository to return "Not found" if user doesn't have access + - Fix check if user is allowed to view the file attachment + - Fix import check for case sensetive namespaces + - Increase timeout for Git-over-HTTP requests to 1 hour since large pulls/pushes can take a long time. + - Properly handle autosave local storage exceptions. + - Escape wildcards when searching LDAP by username. + +v 7.8.1 + - Fix run of custom post receive hooks + - Fix migration that caused issues when upgrading to version 7.8 from versions prior to 7.3 + - Fix the warning for LDAP users about need to set password + - Fix avatars which were not shown for non logged in users + - Fix urls for the issues when relative url was enabled + +v 7.8.0 + - Fix access control and protection against XSS for note attachments and other uploads. + - Replace highlight.js with rouge-fork rugments (Stefan Tatschner) + - Make project search case insensitive (Hannes Rosenögger) + - Include issue/mr participants in list of recipients for reassign/close/reopen emails + - Expose description in groups API + - Better UI for project services page + - Cleaner UI for web editor + - Add diff syntax highlighting in email-on-push service notifications (Hannes Rosenögger) + - Add API endpoint to fetch all changes on a MergeRequest (Jeroen van Baarsen) + - View note image attachments in new tab when clicked instead of downloading them + - Improve sorting logic in UI and API. Explicitly define what sorting method is used by default + - Fix overflow at sidebar when have several items + - Add notes for label changes in issue and merge requests + - Show tags in commit view (Hannes Rosenögger) + - Only count a user's vote once on a merge request or issue (Michael Clarke) + - Increase font size when browse source files and diffs + - Service Templates now let you set default values for all services + - Create new file in empty repository using GitLab UI + - Ability to clone project using oauth2 token + - Upgrade Sidekiq gem to version 3.3.0 + - Stop git zombie creation during force push check + - Show success/error messages for test setting button in services + - Added Rubocop for code style checks + - Fix commits pagination + - Async load a branch information at the commit page + - Disable blacklist validation for project names + - Allow configuring protection of the default branch upon first push (Marco Wessel) + - Add gitlab.com importer + - Add an ability to login with gitlab.com + - Add a commit calendar to the user profile (Hannes Rosenögger) + - Submit comment on command-enter + - Notify all members of a group when that group is mentioned in a comment, for example: `@gitlab-org` or `@sales`. + - Extend issue clossing pattern to include "Resolve", "Resolves", "Resolved", "Resolving" and "Close" (Julien Bianchi and Hannes Rosenögger) + - Fix long broadcast message cut-off on left sidebar (Visay Keo) + - Add Project Avatars (Steven Thonus and Hannes Rosenögger) + - Password reset token validity increased from 2 hours to 2 days since it is also send on account creation. + - Edit group members via API + - Enable raw image paste from clipboard, currently Chrome only (Marco Cyriacks) + - Add action property to merge request hook (Julien Bianchi) + - Remove duplicates from group milestone participants list. + - Add a new API function that retrieves all issues assigned to a single milestone (Justin Whear and Hannes Rosenögger) + - API: Access groups with their path (Julien Bianchi) + - Added link to milestone and keeping resource context on smaller viewports for issues and merge requests (Jason Blanchard) + - Allow notification email to be set separately from primary email. + - API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger) + - Don't have Markdown preview fail for long comments/wiki pages. + - When test web hook - show error message instead of 500 error page if connection to hook url was reset + - Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov) + - Added persistent collapse button for left side nav bar (Jason Blanchard) + - Prevent losing unsaved comments by automatically restoring them when comment page is loaded again. + - Don't allow page to be scaled on mobile. + - Clean the username acquired from OAuth/LDAP so it doesn't fail username validation and block signing up. + - Show assignees in merge request index page (Kelvin Mutuma) + - Link head panel titles to relevant root page. + - Allow users that signed up via OAuth to set their password in order to use Git over HTTP(S). + - Show users button to share their newly created public or internal projects on twitter + - Add quick help links to the GitLab pricing and feature comparison pages. + - Fix duplicate authorized applications in user profile and incorrect application client count in admin area. + - Make sure Markdown previews always use the same styling as the eventual destination. + - Remove deprecated Group#owner_id from API + - Show projects user contributed to on user page. Show stars near project on user page. + - Improve database performance for GitLab + - Add Asana service (Jeremy Benoist) + - Improve project web hooks with extra data + +v 7.7.2 + - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch + - Fix issue when LDAP user can't login with existing GitLab account + +v 7.7.1 + - Improve mention autocomplete performance + - Show setup instructions for GitHub import if disabled + - Allow use http for OAuth applications + +v 7.7.0 + - Import from GitHub.com feature + - Add Jetbrains Teamcity CI service (Jason Lippert) + - Mention notification level + - Markdown preview in wiki (Yuriy Glukhov) + - Raise group avatar filesize limit to 200kb + - OAuth applications feature + - Show user SSH keys in admin area + - Developer can push to protected branches option + - Set project path instead of project name in create form + - Block Git HTTP access after 10 failed authentication attempts + - Updates to the messages returned by API (sponsored by O'Reilly Media) + - New UI layout with side navigation + - Add alert message in case of outdated browser (IE < 10) + - Added API support for sorting projects + - Update gitlab_git to version 7.0.0.rc14 + - Add API project search filter option for authorized projects + - Fix File blame not respecting branch selection + - Change some of application settings on fly in admin area UI + - Redesign signin/signup pages + - Close standard input in Gitlab::Popen.popen + - Trigger GitLab CI when push tags + - When accept merge request - do merge using sidaekiq job + - Enable web signups by default + - Fixes for diff comments: drag-n-drop images, selecting images + - Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update + - Remove password strength indicator + + + +v 7.6.0 + - Fork repository to groups + - New rugged version + - Add CRON=1 backup setting for quiet backups + - Fix failing wiki restore + - Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable) + - Monokai highlighting style now more faithful to original design (Mark Riedesel) + - Create project with repository in synchrony + - Added ability to create empty repo or import existing one if project does not have repository + - Reactivate highlight.js language autodetection + - Mobile UI improvements + - Change maximum avatar file size from 100KB to 200KB + - Strict validation for snippet file names + - Enable Markdown preview for issues, merge requests, milestones, and notes (Vinnie Okada) + - In the docker directory is a container template based on the Omnibus packages. + - Update Sidekiq to version 2.17.8 + - Add author filter to project issues and merge requests pages + - Atom feed for user activity + - Support multiple omniauth providers for the same user + - Rendering cross reference in issue title and tooltip for merge request + - Show username in comments + - Possibility to create Milestones or Labels when Issues are disabled + - Fix bug with showing gpg signature in tag + +v 7.5.3 + - Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2) + +v 7.5.2 + - Don't log Sidekiq arguments by default + - Fix restore of wiki repositories from backups + +v 7.5.1 + - Add missing timestamps to 'members' table +v 7.5.3 + - Update Sidekiq to version 2.17.8 +v 7.5.1 + - Add missing timestamps to 'members' table +v 7.5.3 + - Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2) + +v 7.5.2 + - Don't log Sidekiq arguments by default + - Fix restore of wiki repositories from backups + +v 7.5.1 + - Add missing timestamps to 'members' table + +1. Add "replace" functionality, user can replace existing file in the current repository. +2. Add "upload" functionality, user can upload file into current repository. + +1. Add "replace" functionality, user can replace existing file in the current repository. +2. Add "upload" functionality, user can upload file into current repository. + v 7.5.0 - API: Add support for Hipchat (Kevin Houdebert) - - Add time zone configuration on gitlab.yml (Sullivan Senechal) + - Add time zone configuration in gitlab.yml (Sullivan Senechal) - Fix LDAP authentication for Git HTTP access - Run 'GC.start' after every EmailsOnPushWorker job - Fix LDAP config lookup for provider 'ldap' @@ -16,15 +345,15 @@ v 7.5.0 - Performance improvements - Fix post-receive issue for projects with deleted forks - New gitlab-shell version with custom hooks support - - Improve code + - Improve code - GitLab CI 5.2+ support (does not support older versions) - Fixed bug when you can not push commits starting with 000000 to protected branches - Added a password strength indicator - Change project name and path in one form - Display renamed files in diff views (Vinnie Okada) - - Add timezone configuration to gitlab.yml - Fix raw view for public snippets - Use secret token with GitLab internal API. + - Add missing timestamps to 'members' table v 7.4.3 - Fix raw snippets view @@ -51,6 +380,7 @@ v 7.4.0 - Do not delete tmp/repositories itself during clean-up, only its contents - Support for backup uploads to remote storage - Prevent notes polling when there are not notes + - Internal ForkService: Prepare support for fork to a given namespace - API: Add support for forking a project via the API (Bernhard Kaindl) - API: filter project issues by milestone (Julien Bianchi) - Fail harder in the backup script diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 71435bc600d820df649fbb076311c35b7e2f1bfb..42b5ce22e3249326e9ecbac40614083841f33e83 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,13 @@ Please treat our volunteers with courtesy and respect, it will go a long way tow Issues and merge requests should be in English and contain appropriate language for audiences of all ages. +## Helping others + +Please help other GitLab users when you can. +The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). +Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel. +You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. + ## Issue tracker To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://about.gitlab.com/subscription/) and [consulting services](http://about.gitlab.com/consultancy/) are available from [GitLab.com](http://about.gitlab.com/). @@ -37,7 +44,7 @@ Please send a merge request with a tested solution or a merge request with a fai **[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post): 1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen) -1. **Steps to reproduce:** How can we reproduce the issue, preferably on the [GitLab development virtual machine with vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) (start your issue with: `vagrant destroy && vagrant up && vagrant ssh`) +1. **Steps to reproduce:** How can we reproduce the issue 1. **Expected behavior:** Describe your issue in detail 1. **Observed behavior** 1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise. @@ -56,6 +63,8 @@ Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. +To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file. + ### Merge request guidelines If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows: @@ -63,7 +72,7 @@ If you can, please submit a merge request with the fix or improvements including 1. Fork the project on GitLab Cloud 1. Create a feature branch 1. Write [tests](README.md#run-the-tests) and code -1. Add your changes to the [CHANGELOG](CHANGELOG) insert your line at a [random point](doc/workflow/gitlab_flow.md#do-not-order-commits-with-rebase) in the current version +1. Add your changes to the [CHANGELOG](CHANGELOG) 1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message 1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) 1. Push the commit to your fork @@ -75,20 +84,49 @@ If you can, please submit a merge request with the fix or improvements including 1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). +1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk. The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features. Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it. -For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the following contribution acceptance criteria. +For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the contribution acceptance criteria. + +## Definition of done + +If you contribute to GitLab please know that changes involve more than just code. +We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html). +Please ensure you support the feature you contribute through all of these steps. + +1. Description explaning the relevancy (see following item) +1. Working and clean code that is commented where needed +1. Unit and integration tests that pass on the CI server +1. Documented in the /doc directory +1. Changelog entry added +1. Reviewed and any concerns are addressed +1. Merged by the project lead +1. Added to the release blog article +1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/) if relevant +1. Community questions answered +1. Answers to questions radiated (in docs/wiki/etc.) + +If you add a dependency in GitLab (such as an operating system package) please consider updating the following and note the applicability of each in your merge request: -**Please format your merge request description as follows:** +1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ +1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md +1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool +1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies +1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit +1. Test suite https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/configure_a_runner_to_run_the_gitlab_ce_test_suite.md +1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab + +## Merge request description format 1. What does this MR do? 1. Are there points in the code the reviewer needs to double check? 1. Why was this MR needed? 1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)? -1. Screenshots (If appropriate) +1. Screenshots (if relevant) ## Contribution acceptance criteria @@ -99,6 +137,7 @@ For examples of feedback on merge requests please look at already [closed merge 1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server) 1. Does not break any existing functionality 1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed) +1. Migrations should do only one thing (eg: either create a table, move data to a new table or remove an old table) to aid retrying on failure 1. Keeps the GitLab code base clean and well structured 1. Contains functionality we think other users will benefit from too 1. Doesn't add configuration options since they complicate future changes @@ -121,5 +160,20 @@ For examples of feedback on merge requests please look at already [closed merge 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) 1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) +1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). + +## Code of conduct +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. + +Instances of abusive, harassing, or otherwise unacceptable behavior can be +reported by emailing contact@gitlab.com + +This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index ccbccc3dc62631f22ff358ac418e52401ec770b4..e70b4523ae7ffe8aa3cac8ecd1b093fba5a98737 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.2.0 +2.6.0 diff --git a/Gemfile b/Gemfile index bb8aef65d2fef6fb87ad9d4a3e0864d5919ec593..924fef71fea84368c197f7cd9a76eea1ec961c10 100644 --- a/Gemfile +++ b/Gemfile @@ -28,22 +28,30 @@ gem 'omniauth-google-oauth2' gem 'omniauth-twitter' gem 'omniauth-github' gem 'omniauth-shibboleth' +gem 'omniauth-kerberos' +gem 'omniauth-gitlab' +gem 'omniauth-bitbucket' +gem 'doorkeeper', '2.1.3' +gem "rack-oauth2", "~> 1.0.5" + +# Browser detection +gem "browser" # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '7.0.0.rc11' +gem "gitlab_git", '~> 7.1.3' # Ruby/Rack Git Smart-HTTP Server Handler -gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' +gem 'gitlab-grack', '~> 2.0.0.rc2', require: 'grack' # LDAP Auth -gem 'gitlab_omniauth-ldap', '1.2.0', require: "omniauth-ldap" +gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap" # Git Wiki -gem 'gollum-lib', '~> 3.0.0' +gem 'gollum-lib', '~> 4.0.2' # Language detection -gem "gitlab-linguist", "~> 3.0.0", require: "linguist" +gem "gitlab-linguist", "~> 3.0.1", require: "linguist" # API gem "grape", "~> 0.6.1" @@ -80,7 +88,7 @@ gem "six" gem "seed-fu" # Markup pipeline for GitLab -gem 'html-pipeline-gitlab', '~> 0.1.0' +gem 'html-pipeline-gitlab', '~> 0.1' # Markdown to HTML gem "github-markup" @@ -89,7 +97,7 @@ gem "github-markup" gem 'redcarpet', '~> 3.1.2' gem 'RedCloth' gem 'rdoc', '~>3.6' -gem 'org-ruby', '= 0.9.9' +gem 'org-ruby', '= 0.9.12' gem 'creole', '~>0.3.6' gem 'wikicloth', '=0.8.1' gem 'asciidoctor', '= 0.1.4' @@ -107,12 +115,15 @@ end gem "state_machine" # Issue tags -gem "acts-as-taggable-on" +gem 'acts-as-taggable-on', '~> 3.4' # Background jobs gem 'slim' gem 'sinatra', require: nil -gem 'sidekiq', '2.17.0' +gem 'sidekiq', '~> 3.3' +gem 'sidetiq', '0.6.3' +Update Sidekiq to 2.17.8 +Revert "Update Sidekiq to 2.17.8" # HTTP requests gem "httparty" @@ -134,7 +145,7 @@ gem "redis-rails" gem 'tinder', '~> 1.9.2' # HipChat integration -gem "hipchat", "~> 0.14.0" +gem "hipchat", "~> 1.4.0" # Flowdock integration gem "gitlab-flowdock-git-hook", "~> 0.4.2" @@ -145,9 +156,15 @@ gem "gemnasium-gitlab-service", "~> 0.2" # Slack integration gem "slack-notifier", "~> 1.0.0" +# Asana integration +gem 'asana', '~> 0.0.6' + # d3 gem "d3_rails", "~> 3.1.4" +#cal-heatmap +gem "cal-heatmap-rails", "~> 0.0.1" + # underscore-rails gem "underscore-rails", "~> 1.4.4" @@ -163,13 +180,12 @@ gem 'ace-rails-ap' # Keyboard shortcuts gem 'mousetrap-rails' -# Semantic UI Sass for Sidebar -gem 'semantic-ui-sass', '~> 0.16.1.0' +# Detect and convert string character encoding +gem 'charlock_holmes' gem "sass-rails", '~> 4.0.2' gem "coffee-rails" gem "uglifier" -gem "therubyracer" gem 'turbolinks' gem 'jquery-turbolinks' @@ -181,7 +197,7 @@ gem "jquery-scrollto-rails" gem "raphael-rails", "~> 2.1.2" gem 'bootstrap-sass', '~> 3.0' gem "font-awesome-rails", '~> 4.2' -gem "gitlab_emoji", "~> 0.0.1.1" +gem "gitlab_emoji", "~> 0.1" gem "gon", '~> 5.0.0' gem 'nprogress-rails' gem 'request_store' @@ -189,17 +205,17 @@ gem "virtus" gem 'addressable' group :development do + gem 'brakeman', require: false gem "annotate", "~> 2.6.0.beta2" gem "letter_opener" gem 'quiet_assets', '~> 1.0.1' gem 'rack-mini-profiler', require: false + gem "byebug" # Better errors handler gem 'better_errors' gem 'binding_of_caller' - gem 'rails_best_practices' - # Docs generator gem "sdoc" @@ -209,11 +225,12 @@ end group :development, :test do gem 'coveralls', require: false + gem 'rubocop', '0.28.0', require: false # gem 'rails-dev-tweaks' gem 'spinach-rails' - gem "rspec-rails" + gem "rspec-rails", '2.99' gem "capybara", '~> 2.2.1' - gem "pry" + gem "pry-rails" gem "awesome_print" gem "database_cleaner" gem "launchy" @@ -239,14 +256,14 @@ group :development, :test do gem 'jasmine', '2.0.2' - gem "spring", '1.1.3' - gem "spring-commands-rspec", '1.0.1' + gem "spring", '~> 1.3.1' + gem "spring-commands-rspec", '1.0.4' gem "spring-commands-spinach", '1.0.0' end group :test do gem "simplecov", require: false - gem "shoulda-matchers", "~> 2.1.0" + gem "shoulda-matchers", "~> 2.7.0" gem 'email_spec' gem "webmock" gem 'test_after_commit' @@ -257,3 +274,6 @@ group :production do end gem "newrelic_rpm" + +gem 'octokit', '3.7.0' +gem "rugments" diff --git a/Gemfile.lock b/Gemfile.lock index a3645f7bbec99c6c9fd3ae1124e668604f097a0e..60777cf312b342ba7afa9fd639aed638b4060088 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,40 +3,53 @@ GEM specs: RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.1) - actionpack (= 4.1.1) - actionview (= 4.1.1) - mail (~> 2.5.4) - actionpack (4.1.1) - actionview (= 4.1.1) - activesupport (= 4.1.1) + actionmailer (4.1.9) + actionpack (= 4.1.9) + actionview (= 4.1.9) + mail (~> 2.5, >= 2.5.4) + actionpack (4.1.9) + actionview (= 4.1.9) + activesupport (= 4.1.9) rack (~> 1.5.2) rack-test (~> 0.6.2) - actionview (4.1.1) - activesupport (= 4.1.1) + actionview (4.1.9) + activesupport (= 4.1.9) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.1) - activesupport (= 4.1.1) + activemodel (4.1.9) + activesupport (= 4.1.9) builder (~> 3.1) - activerecord (4.1.1) - activemodel (= 4.1.1) - activesupport (= 4.1.1) + activerecord (4.1.9) + activemodel (= 4.1.9) + activesupport (= 4.1.9) arel (~> 5.0.0) - activesupport (4.1.1) + activeresource (4.0.0) + activemodel (~> 4.0) + activesupport (~> 4.0) + rails-observers (~> 0.1.1) + activesupport (4.1.9) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) - acts-as-taggable-on (2.4.1) - rails (>= 3, < 5) + acts-as-taggable-on (3.5.0) + activerecord (>= 3.2, < 5) addressable (2.3.5) annotate (2.6.0) activerecord (>= 2.3.0) rake (>= 0.8.7) arel (5.0.1.20140414130214) + asana (0.0.6) + activeresource (>= 3.2.3) asciidoctor (0.1.4) + ast (2.0.0) + astrolabe (1.3.0) + parser (>= 2.2.0.pre.3, < 3.0) + attr_required (1.0.0) + autoprefixer-rails (5.1.6) + execjs + json awesome_print (1.2.0) axiom-types (0.0.5) descendants_tracker (~> 0.0.1) @@ -47,9 +60,25 @@ GEM erubis (>= 2.6.6) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bootstrap-sass (3.0.3.0) - sass (~> 3.2) + bootstrap-sass (3.3.3) + autoprefixer-rails (>= 5.0.0.1) + sass (>= 3.2.19) + brakeman (3.0.1) + erubis (~> 2.6) + fastercsv (~> 1.5) + haml (>= 3.0, < 5.0) + highline (~> 1.6.20) + multi_json (~> 1.2) + ruby2ruby (~> 2.1.1) + ruby_parser (~> 3.5.0) + sass (~> 3.0) + terminal-table (~> 1.4) + browser (0.7.2) builder (3.2.2) + byebug (3.2.0) + columnize (~> 0.8) + debugger-linecache (~> 1.2) + cal-heatmap-rails (0.0.1) capybara (2.2.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -60,12 +89,10 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.15.2) - timers (~> 1.1.0) + celluloid (0.16.0) + timers (~> 4.0.0) charlock_holmes (0.6.9.4) cliver (0.3.2) - code_analyzer (0.4.3) - sexp_processor coderay (1.1.0) coercible (1.0.0) descendants_tracker (~> 0.0.1) @@ -78,7 +105,10 @@ GEM coffee-script-source (1.6.3) colored (1.2) colorize (0.5.8) - connection_pool (1.2.0) + columnize (0.9.0) + Update Sidekiq to 2.17.8 + connection_pool (2.1.0) + Revert "Update Sidekiq to 2.17.8" coveralls (0.7.0) multi_json (~> 1.3) rest-client @@ -93,6 +123,7 @@ GEM daemons (1.1.9) database_cleaner (1.3.0) debug_inspector (0.0.2) + debugger-linecache (1.2.0) default_value_for (3.0.0) activerecord (>= 3.2.0, < 5.0) descendants_tracker (0.0.3) @@ -107,20 +138,20 @@ GEM diff-lcs (1.2.5) diffy (3.0.3) docile (1.1.5) + doorkeeper (2.1.3) + railties (>= 3.2) dotenv (0.9.0) dropzonejs-rails (0.4.14) rails (> 3.1) email_spec (1.5.0) launchy (~> 2.1) mail (~> 2.2) - emoji (1.0.1) - json enumerize (0.7.0) activesupport (>= 3.2) equalizer (0.0.8) erubis (2.7.0) escape_utils (0.2.4) - eventmachine (1.0.3) + eventmachine (1.0.4) excon (0.32.1) execjs (2.0.2) expression_parser (0.9.0) @@ -133,6 +164,7 @@ GEM multipart-post (~> 1.2.0) faraday_middleware (0.9.0) faraday (>= 0.7.4, < 0.9) + fastercsv (1.5.5) ffaker (1.22.1) ffi (1.9.3) fog (1.21.0) @@ -158,43 +190,48 @@ GEM dotenv (>= 0.7) thor (>= 0.13.6) formatador (0.2.4) - gemnasium-gitlab-service (0.2.2) - rugged (~> 0.19) + gemnasium-gitlab-service (0.2.4) + rugged (~> 0.21) + gemojione (2.0.0) + json gherkin-ruby (0.3.1) racc - github-markup (1.1.0) + github-markup (1.3.1) + posix-spawn (~> 0.3.8) gitlab-flowdock-git-hook (0.4.2.2) gitlab-grit (>= 2.4.1) multi_json - gitlab-grack (2.0.0.pre) + gitlab-grack (2.0.0) rack (~> 1.5.1) - gitlab-grit (2.6.12) + gitlab-grit (2.7.2) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) mime-types (~> 1.15) posix-spawn (~> 0.3) - gitlab-linguist (3.0.0) + gitlab-linguist (3.0.1) charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.4) mime-types (~> 1.19) - gitlab_emoji (0.0.1.1) - emoji (~> 1.0.1) - gitlab_git (7.0.0.rc11) + gitlab_emoji (0.1.0) + gemojione (~> 2.0) + gitlab_git (7.1.3) activesupport (~> 4.0) charlock_holmes (~> 0.6) gitlab-linguist (~> 3.0) - rugged (~> 0.21.0) + rugged (~> 0.21.2) gitlab_meta (7.0) - gitlab_omniauth-ldap (1.2.0) + gitlab_omniauth-ldap (1.2.1) net-ldap (~> 0.9) omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.3) - gollum-lib (3.0.0) - github-markup (~> 1.1.0) - gitlab-grit (~> 2.6.5) - nokogiri (~> 1.6.1) - rouge (~> 1.3.3) + gollum-grit_adapter (0.1.3) + gitlab-grit (~> 2.7, >= 2.7.1) + gollum-lib (4.0.2) + github-markup (~> 1.3.1) + gollum-grit_adapter (~> 0.1, >= 0.1.1) + nokogiri (~> 1.6.4) + rouge (~> 1.7.4) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (5.0.1) @@ -234,24 +271,28 @@ GEM haml (>= 3.1, < 5.0) railties (>= 4.0.1) hashie (2.1.2) + highline (1.6.21) hike (1.2.3) - hipchat (0.14.0) - httparty + hipchat (1.4.0) httparty + hitimes (1.2.2) html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) - html-pipeline-gitlab (0.1.5) + html-pipeline-gitlab (0.2.0) actionpack (~> 4) - gitlab_emoji (~> 0.0.1) + gitlab_emoji (~> 0.1) html-pipeline (~> 1.11.0) + mime-types sanitize (~> 2.1) http_parser.rb (0.5.3) httparty (0.13.0) json (~> 1.8) multi_xml (>= 0.5.2) httpauth (0.2.1) - i18n (0.6.11) + httpclient (2.5.3.3) + i18n (0.7.0) + ice_cube (0.11.1) ice_nine (0.10.0) jasmine (2.0.2) jasmine-core (~> 2.0.0) @@ -270,42 +311,40 @@ GEM turbolinks jquery-ui-rails (4.2.1) railties (>= 3.2.16) - json (1.8.1) + json (1.8.2) jwt (0.1.13) multi_json (>= 1.5) kaminari (0.15.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.8.1) + kgio (2.9.2) launchy (2.4.2) addressable (~> 2.3) letter_opener (1.1.2) launchy (~> 2.2) - libv8 (3.16.14.3) listen (2.3.1) celluloid (>= 0.15.2) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) lumberjack (1.0.4) - mail (2.5.4) - mime-types (~> 1.16) - treetop (~> 1.4.8) + mail (2.6.3) + mime-types (>= 1.16, < 3) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.6.0) + mini_portile (0.6.1) minitest (5.3.5) mousetrap-rails (1.4.6) multi_json (1.10.1) multi_xml (0.5.5) multipart-post (1.2.0) mysql2 (0.3.16) - net-ldap (0.9.0) + net-ldap (0.11) net-scp (1.1.2) net-ssh (>= 2.6.5) net-ssh (2.8.0) newrelic_rpm (3.9.4.245) - nokogiri (1.6.2.1) - mini_portile (= 0.6.0) + nokogiri (1.6.5) + mini_portile (~> 0.6.0) nprogress-rails (0.1.2.3) oauth (0.4.7) oauth2 (0.8.1) @@ -314,15 +353,29 @@ GEM jwt (~> 0.1.4) multi_json (~> 1.0) rack (~> 1.2) + octokit (3.7.0) + sawyer (~> 0.6.0, >= 0.5.3) omniauth (1.1.4) hashie (>= 1.2, < 3) rack + omniauth-bitbucket (0.0.2) + multi_json (~> 1.7) + omniauth (~> 1.1) + omniauth-oauth (~> 1.0) omniauth-github (1.1.1) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) + omniauth-gitlab (1.0.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.0) omniauth-google-oauth2 (0.2.5) omniauth (> 1.0) omniauth-oauth2 (~> 1.1) + omniauth-kerberos (0.2.0) + omniauth-multipassword + timfel-krb5-auth (~> 0.8) + omniauth-multipassword (0.4.1) + omniauth (~> 1.0) omniauth-oauth (1.0.1) oauth omniauth (~> 1.0) @@ -334,9 +387,11 @@ GEM omniauth-twitter (1.0.1) multi_json (~> 1.3) omniauth-oauth (~> 1.0) - org-ruby (0.9.9) + org-ruby (0.9.12) rubypants (~> 0.2) orm_adapter (0.5.0) + parser (2.2.0.2) + ast (>= 1.1, < 3.0) pg (0.15.1) phantomjs (1.9.2.0) poltergeist (1.5.1) @@ -344,12 +399,14 @@ GEM cliver (~> 0.3.1) multi_json (~> 1.0) websocket-driver (>= 0.2.0) - polyglot (0.3.4) posix-spawn (0.3.9) + powerpack (0.0.9) pry (0.9.12.4) coderay (~> 1.0) method_source (~> 0.8) slop (~> 3.4) + pry-rails (0.3.2) + pry (>= 0.9.10) pyu-ruby-sasl (0.0.3.3) quiet_assets (1.0.2) railties (>= 3.1, < 5.0) @@ -357,45 +414,45 @@ GEM rack (1.5.2) rack-accept (0.4.5) rack (>= 0.4) - rack-attack (2.3.0) + rack-attack (4.2.0) rack rack-cors (0.2.9) rack-mini-profiler (0.9.0) rack (>= 1.1.3) rack-mount (0.8.3) rack (>= 1.0.0) + rack-oauth2 (1.0.8) + activesupport (>= 2.3) + attr_required (>= 0.0.5) + httpclient (>= 2.2.0.2) + multi_json (>= 1.3.6) + rack (>= 1.1) rack-protection (1.5.1) rack - rack-test (0.6.2) + rack-test (0.6.3) rack (>= 1.0) - rails (4.1.1) - actionmailer (= 4.1.1) - actionpack (= 4.1.1) - actionview (= 4.1.1) - activemodel (= 4.1.1) - activerecord (= 4.1.1) - activesupport (= 4.1.1) + rails (4.1.9) + actionmailer (= 4.1.9) + actionpack (= 4.1.9) + actionview (= 4.1.9) + activemodel (= 4.1.9) + activerecord (= 4.1.9) + activesupport (= 4.1.9) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.1) + railties (= 4.1.9) sprockets-rails (~> 2.0) + rails-observers (0.1.2) + activemodel (~> 4.0) rails_autolink (1.1.6) rails (> 3.1) - rails_best_practices (1.14.4) - activesupport - awesome_print - code_analyzer (>= 0.4.3) - colored - erubis - i18n - require_all - ruby-progressbar - railties (4.1.1) - actionpack (= 4.1.1) - activesupport (= 4.1.1) + railties (4.1.9) + actionpack (= 4.1.9) + activesupport (= 4.1.9) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - raindrops (0.12.0) - rake (10.3.2) + rainbow (2.0.0) + raindrops (0.13.0) + rake (10.4.2) raphael-rails (2.1.2) rb-fsevent (0.9.3) rb-inotify (0.9.2) @@ -422,32 +479,46 @@ GEM redis-store (~> 1.1.0) redis-store (1.1.4) redis (>= 2.2) - ref (1.0.5) request_store (1.0.5) - require_all (1.3.2) rest-client (1.6.7) mime-types (>= 1.16) rinku (1.7.3) - rouge (1.3.3) - rspec (2.14.1) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) - rspec-core (2.14.7) - rspec-expectations (2.14.4) + rouge (1.7.7) + rspec (2.99.0) + rspec-core (~> 2.99.0) + rspec-expectations (~> 2.99.0) + rspec-mocks (~> 2.99.0) + rspec-collection_matchers (1.1.2) + rspec-expectations (>= 2.99.0.beta1) + rspec-core (2.99.2) + rspec-expectations (2.99.2) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.4) - rspec-rails (2.14.0) + rspec-mocks (2.99.3) + rspec-rails (2.99.0) actionpack (>= 3.0) + activemodel (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) - ruby-progressbar (1.2.0) - rubyntlm (0.4.0) + rspec-collection_matchers + rspec-core (~> 2.99.0) + rspec-expectations (~> 2.99.0) + rspec-mocks (~> 2.99.0) + rubocop (0.28.0) + astrolabe (~> 1.3) + parser (>= 2.2.0.pre.7, < 3.0) + powerpack (~> 0.0.6) + rainbow (>= 1.99.1, < 3.0) + ruby-progressbar (~> 1.4) + ruby-progressbar (1.7.1) + ruby2ruby (2.1.3) + ruby_parser (~> 3.1) + sexp_processor (~> 4.0) + ruby_parser (3.5.0) + sexp_processor (~> 4.1) + rubyntlm (0.5.0) rubypants (0.2.0) - rugged (0.21.0) + rugged (0.21.4) + rugments (1.0.0.beta6) safe_yaml (0.9.7) sanitize (2.1.0) nokogiri (>= 1.4.4) @@ -457,6 +528,9 @@ GEM sass (~> 3.2.0) sprockets (~> 2.8, <= 2.11.0) sprockets-rails (~> 2.0) + sawyer (0.6.0) + addressable (~> 2.3.5) + faraday (~> 0.8, < 0.10) sdoc (0.3.20) json (>= 1.1.3) rdoc (~> 3.10) @@ -465,18 +539,38 @@ GEM activesupport (>= 3.1, < 4.2) select2-rails (3.5.2) thor (~> 0.14) - semantic-ui-sass (0.16.1.0) - sass (~> 3.2) settingslogic (2.0.9) - sexp_processor (4.4.0) - shoulda-matchers (2.1.0) + sexp_processor (4.4.5) + shoulda-matchers (2.7.0) activesupport (>= 3.0.0) +<<<<<<< HEAD +<<<<<<< HEAD + sidekiq (3.3.0) + celluloid (>= 0.16.0) + connection_pool (>= 2.0.0) + json + redis (>= 3.0.6) + redis-namespace (>= 1.3.1) + sidetiq (0.6.3) + celluloid (>= 0.14.1) + ice_cube (= 0.11.1) + sidekiq (>= 3.0.0) +======= + sidekiq (2.17.8) + celluloid (= 0.15.2) + connection_pool (~> 2.0) + json + redis (~> 3.1) + redis-namespace (~> 1.3) +>>>>>>> Update Sidekiq to 2.17.8 +======= sidekiq (2.17.0) celluloid (>= 0.15.2) connection_pool (>= 1.0.0) json redis (>= 3.0.4) redis-namespace (>= 1.3.1) +>>>>>>> Revert "Update Sidekiq to 2.17.8" simple_oauth (0.1.9) simplecov (0.9.0) docile (~> 1.1.0) @@ -492,7 +586,7 @@ GEM slim (2.0.2) temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) - slop (3.4.7) + slop (3.6.0) spinach (0.8.7) colorize (= 0.5.8) gherkin-ruby (>= 0.3.1) @@ -500,8 +594,8 @@ GEM capybara (>= 2.0.0) railties (>= 3) spinach (>= 0.4) - spring (1.1.3) - spring-commands-rspec (1.0.1) + spring (1.3.3) + spring-commands-rspec (1.0.4) spring (>= 0.9.1) spring-commands-spinach (1.0.0) spring (>= 0.9.1) @@ -510,28 +604,28 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.3) + sprockets-rails (2.2.4) actionpack (>= 3.0) activesupport (>= 3.0) - sprockets (~> 2.8) + sprockets (>= 2.8, < 4.0) stamp (0.5.0) state_machine (1.2.0) - stringex (2.5.1) + stringex (2.5.2) temple (0.6.7) term-ansicolor (1.2.2) tins (~> 0.8) + terminal-table (1.4.5) test_after_commit (0.2.2) - therubyracer (0.12.0) - libv8 (~> 3.16.14.0) - ref thin (1.6.1) daemons (>= 1.0.9) eventmachine (>= 1.0.0) rack (>= 1.0.0) thor (0.19.1) - thread_safe (0.3.4) + thread_safe (0.3.5) tilt (1.4.1) - timers (1.1.0) + timers (4.0.1) + hitimes + timfel-krb5-auth (0.8.3) tinder (1.9.3) eventmachine (~> 1.0) faraday (~> 0.8) @@ -542,9 +636,6 @@ GEM multi_json (~> 1.7) twitter-stream (~> 0.1) tins (0.13.1) - treetop (1.4.15) - polyglot - polyglot (>= 0.3.1) turbolinks (2.0.0) coffee-rails twitter-stream (0.1.16) @@ -566,7 +657,7 @@ GEM raindrops (~> 0.7) unicorn-worker-killer (0.4.2) unicorn (~> 4) - version_sorter (1.1.0) + version_sorter (2.0.0) virtus (1.0.1) axiom-types (~> 0.0.5) coercible (~> 1.0) @@ -591,16 +682,22 @@ PLATFORMS DEPENDENCIES RedCloth ace-rails-ap - acts-as-taggable-on + acts-as-taggable-on (~> 3.4) addressable annotate (~> 2.6.0.beta2) + asana (~> 0.0.6) asciidoctor (= 0.1.4) awesome_print better_errors binding_of_caller bootstrap-sass (~> 3.0) + brakeman + browser + byebug + cal-heatmap-rails (~> 0.0.1) capybara (~> 2.2.1) carrierwave + charlock_holmes coffee-rails colored coveralls @@ -611,6 +708,7 @@ DEPENDENCIES devise (= 3.2.4) devise-async (= 0.9.0) diffy (~> 3.0.3) + doorkeeper (= 2.1.3) dropzonejs-rails email_spec enumerize @@ -622,13 +720,13 @@ DEPENDENCIES gemnasium-gitlab-service (~> 0.2) github-markup gitlab-flowdock-git-hook (~> 0.4.2) - gitlab-grack (~> 2.0.0.pre) - gitlab-linguist (~> 3.0.0) - gitlab_emoji (~> 0.0.1.1) - gitlab_git (= 7.0.0.rc11) + gitlab-grack (~> 2.0.0.rc2) + gitlab-linguist (~> 3.0.1) + gitlab_emoji (~> 0.1) + gitlab_git (~> 7.1.3) gitlab_meta (= 7.0) - gitlab_omniauth-ldap (= 1.2.0) - gollum-lib (~> 3.0.0) + gitlab_omniauth-ldap (= 1.2.1) + gollum-lib (~> 4.0.2) gon (~> 5.0.0) grape (~> 0.6.1) grape-entity (~> 0.4.2) @@ -636,8 +734,8 @@ DEPENDENCIES guard-rspec guard-spinach haml-rails - hipchat (~> 0.14.0) - html-pipeline-gitlab (~> 0.1.0) + hipchat (~> 1.4.0) + html-pipeline-gitlab (~> 0.1) httparty jasmine (= 2.0.2) jquery-atwho-rails (~> 0.3.3) @@ -653,22 +751,26 @@ DEPENDENCIES mysql2 newrelic_rpm nprogress-rails + octokit (= 3.7.0) omniauth (~> 1.1.3) + omniauth-bitbucket omniauth-github + omniauth-gitlab omniauth-google-oauth2 + omniauth-kerberos omniauth-shibboleth omniauth-twitter - org-ruby (= 0.9.9) + org-ruby (= 0.9.12) pg poltergeist (~> 1.5.1) - pry + pry-rails quiet_assets (~> 1.0.1) rack-attack rack-cors rack-mini-profiler + rack-oauth2 (~> 1.0.5) rails (~> 4.1.0) rails_autolink (~> 1.1) - rails_best_practices raphael-rails (~> 2.1.2) rb-fsevent rb-inotify @@ -676,29 +778,39 @@ DEPENDENCIES redcarpet (~> 3.1.2) redis-rails request_store - rspec-rails + rspec-rails (= 2.99) + rubocop (= 0.28.0) + rugments sanitize (~> 2.0) sass-rails (~> 4.0.2) sdoc seed-fu select2-rails - semantic-ui-sass (~> 0.16.1.0) settingslogic +<<<<<<< HEAD + shoulda-matchers (~> 2.7.0) + sidekiq (~> 3.3) + sidetiq (= 0.6.3) +======= shoulda-matchers (~> 2.1.0) +<<<<<<< HEAD + sidekiq (= 2.17.8) +>>>>>>> Update Sidekiq to 2.17.8 +======= sidekiq (= 2.17.0) +>>>>>>> Revert "Update Sidekiq to 2.17.8" simplecov sinatra six slack-notifier (~> 1.0.0) slim spinach-rails - spring (= 1.1.3) - spring-commands-rspec (= 1.0.1) + spring (~> 1.3.1) + spring-commands-rspec (= 1.0.4) spring-commands-spinach (= 1.0.0) stamp state_machine test_after_commit - therubyracer thin tinder (~> 1.9.2) turbolinks diff --git a/LICENSE b/LICENSE index d11b8730bf124354293154ce5f5a317c4935a780..d8cb29f36384fe739136ce15c79f41eb1ef45247 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2014 GitLab B.V. +Copyright (c) 2011-2015 GitLab B.V. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MAINTENANCE.md b/MAINTENANCE.md index 19200fef3891cb830702ff6c2ef25f4aaba616e2..d3d36670693aa4c9b5b51a44e37542b077d2c9cd 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -2,7 +2,7 @@ GitLab is a fast moving and evolving project. We currently don't have the resources to support many releases concurrently. We support exactly one stable release at any given time. -GitLab follows the [Semantic Versioning](http://semver.org/) for its releases: `(Major).(Minor).(Patch)`. +GitLab follows the [Semantic Versioning](http://semver.org/) for its releases: `(Major).(Minor).(Patch)` in a [pragmatic way](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e). - **Major version**: Whenever there is something significant or any backwards incompatible changes are introduced to the public API. - **Minor version**: When new, backwards compatible functionality is introduced to the public API or a minor feature is introduced, or when a set of smaller features is rolled out. diff --git a/PROCESS.md b/PROCESS.md index 1dd28d6b670429b75b5cb35981442dc8acda3379..1b6b3e7d32d053ac6124be3c1637b3f8db12c204 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -71,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue ### Feature requests -Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. +Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the \[feature request forum\]\(http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. ### Issue report for old version @@ -104,3 +104,10 @@ This merge request has been closed because a request for more information has no ### Accepting merge requests Is there a request on [the feature request forum](http://feedback.gitlab.com/forums/176466-general) that is similar to this? If so, can you make a comment with a link to it? Please be aware that new functionality that is not marked [accepting merge/pull requests](http://feedback.gitlab.com/forums/176466-general/status/796455) on the forum might not make it into GitLab. You might be asked to make changes and even after implementing them your feature might still be declined. If you want to reduce the chance of this happening please have a discussion in the forum first. + +### Only accepting merge requests with green tests + +We can only accept a merge request if all the tests are green. I've just +restarted the build. When the tests are still not passing after this restart and +you're sure that is does not have anything to do with your code changes, please +rebase with master to see if that solves the issue. diff --git a/Procfile b/Procfile index a5693f8dbc52a91dc637bfb54d15ca5af4615466..a0ab4a734a4375f69a6b15bc4122ce4dea428f11 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell +worker: bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default diff --git a/README.md b/README.md index 63fa5e3da86f1a2b1054b0584613a4e05a77b365..0563ceca4097c2ed5be75da2bf8177946a736bdd 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,19 @@ - Each project can also have an issue tracker and a wiki - Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises - Completely free and open source (MIT Expat license) -- Powered by Ruby on Rails +- Powered by [Ruby on Rails](https://github.com/rails/rails) + +## Editions + +There are two editions of GitLab. +*GitLab [Community Edition](https://about.gitlab.com/features/) (CE)* is available without any costs under an MIT license. + +*GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users. +To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/). ## Canonical source -- The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. +The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. ## Code status @@ -25,8 +33,6 @@ - [](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) -- [](https://www.pullreview.com/gitlab.gitlab.com/gitlab-org/gitlab-ce/reviews/master) - ## Website On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: @@ -40,81 +46,45 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ## Requirements -- Ubuntu/Debian/CentOS/RHEL** -- ruby 2.0+ -- git 1.7.10+ -- redis 2.0+ +GitLab requires the following software: + +- Ubuntu/Debian/CentOS/RHEL +- Ruby (MRI) 2.0 or 2.1 +- Git 1.7.10+ +- Redis 2.0+ - MySQL or PostgreSQL -** More details are in the [requirements doc](doc/install/requirements.md). +Please see the [requirements documentation](doc/install/requirements.md) for system requirements and more information about the supported operating systems. ## Installation -Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options. -Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm). - -## Third-party applications +The recommended way to install GitLab is using the provided [Omnibus packages](https://about.gitlab.com/downloads/). Compared to an installation from source, this is faster and less error prone. Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager. -There are a lot of applications and API wrappers for GitLab. -Find them [on our website](https://about.gitlab.com/applications/). +There are various other options to install GitLab, please refer to the [installation page on the GitLab website](https://about.gitlab.com/installation/) for more information. -### New versions +You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password. -Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). - -### Upgrading - -For updating the the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For manual installations there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update). - -## Run in production mode +## Third-party applications -The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually: +There are a lot of [third-party applications integrating with GitLab](https://about.gitlab.com/applications/). These include GUI Git clients, mobile applications and API wrappers for various languages. - sudo service gitlab start +## GitLab release cycle -or by directly calling the script: +Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). - sudo /etc/init.d/gitlab start +## Upgrading -Please login with `root` / `5iveL!fe` +For updating the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For installations from source there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update) detailing all necessary commands to migrate to the next version. ## Install a development environment -We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). -If you do not use the development kit you might need to copy the example development unicorn configuration file +To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). +If you do not use the GitLab Development Kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone. +One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file: cp config/unicorn.rb.example.development config/unicorn.rb -## Run in development mode - -Start it with [Foreman](https://github.com/ddollar/foreman) - - bundle exec foreman start -p 3000 - -or start each component separately: - - bundle exec rails s - bin/background_jobs start - -And surf to [localhost:3000](http://localhost:3000/) and login with `root` / `5iveL!fe`. - -## Run the tests - -- Run all tests: - - bundle exec rake test - -- [RSpec](http://rspec.info/) unit and functional tests. - - All RSpec tests: `bundle exec rake spec` - - Single RSpec file: `bundle exec rspec spec/controllers/commit_controller_spec.rb` - -- [Spinach](https://github.com/codegram/spinach) integration tests. - - All Spinach tests: `bundle exec rake spinach` - - Single Spinach test: `bundle exec spinach features/project/issues/milestones.feature` +Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). ## Documentation @@ -131,4 +101,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on ## Is it awesome? Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua. -[These people](https://twitter.com/gitlabhq/favorites) seem to like it. +[These people](https://twitter.com/gitlab/favorites) seem to like it. diff --git a/VERSION b/VERSION index 027a8b7b3321de450a2602be95cfff28827c3704..25df36fff9d87b75312d5a06d98f9d84340f854d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.5.0.pre +Version 7.5.3 diff --git a/app/assets/images/authbuttons/bitbucket_64.png b/app/assets/images/authbuttons/bitbucket_64.png new file mode 100644 index 0000000000000000000000000000000000000000..4b90a57bc7de93163275f99ea2971e2740af98af Binary files /dev/null and b/app/assets/images/authbuttons/bitbucket_64.png differ diff --git a/app/assets/images/authbuttons/github_32.png b/app/assets/images/authbuttons/github_32.png deleted file mode 100644 index c56eef05eb981c1e2361312355dceecac3a7b5a4..0000000000000000000000000000000000000000 Binary files a/app/assets/images/authbuttons/github_32.png and /dev/null differ diff --git a/app/assets/images/authbuttons/github_64.png b/app/assets/images/authbuttons/github_64.png index 39de55bc7964dd263adfebcdd3f7260d7d7e21c0..dc7c03d10052a596f062207ae0fd8e9cf384b2aa 100644 Binary files a/app/assets/images/authbuttons/github_64.png and b/app/assets/images/authbuttons/github_64.png differ diff --git a/app/assets/images/authbuttons/gitlab_64.png b/app/assets/images/authbuttons/gitlab_64.png new file mode 100644 index 0000000000000000000000000000000000000000..31281a194441d4e23bb691de89110c6097021ac9 Binary files /dev/null and b/app/assets/images/authbuttons/gitlab_64.png differ diff --git a/app/assets/images/authbuttons/google_32.png b/app/assets/images/authbuttons/google_32.png deleted file mode 100644 index 6225cc9c2d77b8be6b060d212e1624b86935a46c..0000000000000000000000000000000000000000 Binary files a/app/assets/images/authbuttons/google_32.png and /dev/null differ diff --git a/app/assets/images/authbuttons/google_64.png b/app/assets/images/authbuttons/google_64.png index 4d608f710089bb406f9b3567270354434e7a58cd..94a0e089c6efe07dcb50af341d51c2d13b689639 100644 Binary files a/app/assets/images/authbuttons/google_64.png and b/app/assets/images/authbuttons/google_64.png differ diff --git a/app/assets/images/authbuttons/twitter_32.png b/app/assets/images/authbuttons/twitter_32.png deleted file mode 100644 index 696eb02484d27ee6d453cbb07189c9923780d24b..0000000000000000000000000000000000000000 Binary files a/app/assets/images/authbuttons/twitter_32.png and /dev/null differ diff --git a/app/assets/images/authbuttons/twitter_64.png b/app/assets/images/authbuttons/twitter_64.png index 2893274766f8bcfe2c3ee7604d912122ca9b1be3..5c9f14cb0771005efaa2ed050560298aa03131b3 100644 Binary files a/app/assets/images/authbuttons/twitter_64.png and b/app/assets/images/authbuttons/twitter_64.png differ diff --git a/app/assets/images/bg-header.png b/app/assets/images/bg-header.png index 9ecdaf4e2d50de747f5249860f10d6debc33cf4e..639271c6fafc58a2007f22478ba2d52e57c2a2ee 100644 Binary files a/app/assets/images/bg-header.png and b/app/assets/images/bg-header.png differ diff --git a/app/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png index d9066ad7d7b2e7f339bbf92c9968fd9ccd96887d..e5fe659ba63db076784a420b69bf188d898ef52f 100644 Binary files a/app/assets/images/bg_fallback.png and b/app/assets/images/bg_fallback.png differ diff --git a/app/assets/images/brand_logo.png b/app/assets/images/brand_logo.png index 09b1689ca455aaed6e30f7075a7972b8232fa75a..9c564bb61411bf4f0dec3e21aa86848ac70cbcb8 100644 Binary files a/app/assets/images/brand_logo.png and b/app/assets/images/brand_logo.png differ diff --git a/app/assets/images/chosen-sprite.png b/app/assets/images/chosen-sprite.png index d08e4b7e624c44f4fb862f23f046262780847490..3d936b07d443fb420a71cac72750537bb54ad2cd 100644 Binary files a/app/assets/images/chosen-sprite.png and b/app/assets/images/chosen-sprite.png differ diff --git a/app/assets/images/dark-scheme-preview.png b/app/assets/images/dark-scheme-preview.png index 6dac6cd8ca138921421a697b50181abbc357a517..2ef58e52549c6315b66a6dc960e4603de1c00d4b 100644 Binary files a/app/assets/images/dark-scheme-preview.png and b/app/assets/images/dark-scheme-preview.png differ diff --git a/app/assets/images/diff_note_add.png b/app/assets/images/diff_note_add.png index 8ec15b701fc0e806e45dbc10cda9ce068f606a0f..0084422e3303593e2f067c7de013413f393b0f88 100644 Binary files a/app/assets/images/diff_note_add.png and b/app/assets/images/diff_note_add.png differ diff --git a/app/assets/images/gitorious-logo-black.png b/app/assets/images/gitorious-logo-black.png new file mode 100644 index 0000000000000000000000000000000000000000..78f17a9af79dc2395acd9bcc2a391589dd6b6ed8 Binary files /dev/null and b/app/assets/images/gitorious-logo-black.png differ diff --git a/app/assets/images/gitorious-logo-blue.png b/app/assets/images/gitorious-logo-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..4962cffba3153927ddd8cd5ff295fe4ca142bfa8 Binary files /dev/null and b/app/assets/images/gitorious-logo-blue.png differ diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png index 32ade0fe9a37d114ff86789f34242a845f0c5294..60021d5ac47686cce70570d22bad6670082caf56 100644 Binary files a/app/assets/images/icon-link.png and b/app/assets/images/icon-link.png differ diff --git a/app/assets/images/icon-search.png b/app/assets/images/icon-search.png index 084b89e3a7cee5787bc3fae15596dab09c547cd7..3c1c146541d456a042db5768154a307b9b535e9d 100644 Binary files a/app/assets/images/icon-search.png and b/app/assets/images/icon-search.png differ diff --git a/app/assets/images/icon_sprite.png b/app/assets/images/icon_sprite.png index 9ad65fc443bdbbc50fa25797bced0680c8135b5c..2e7a5023398e7aa1d2794755af4f90d59b431919 100644 Binary files a/app/assets/images/icon_sprite.png and b/app/assets/images/icon_sprite.png differ diff --git a/app/assets/images/images.png b/app/assets/images/images.png index da91f6b1f4c31422a3890c3ad8a38bb418a1b81c..ad146246caf907144b468a121ef9524ba8ec3c74 100644 Binary files a/app/assets/images/images.png and b/app/assets/images/images.png differ diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png deleted file mode 100644 index 4a96572d570108366da2cf5aa8213f69b591a2a3..0000000000000000000000000000000000000000 Binary files a/app/assets/images/logo-black.png and /dev/null differ diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png index bc2ef601a538d69ef99d5bdafa605e63f902e8e4..917bcfcb7e750f8426dd79912ca525dc65183a56 100644 Binary files a/app/assets/images/logo-white.png and b/app/assets/images/logo-white.png differ diff --git a/app/assets/images/monokai-scheme-preview.png b/app/assets/images/monokai-scheme-preview.png index 3aeed886a02bcdc0d38467be744887a437fb99c1..fbb339c6a9170de79c5fee8fc2a5abd6a785f4e7 100644 Binary files a/app/assets/images/monokai-scheme-preview.png and b/app/assets/images/monokai-scheme-preview.png differ diff --git a/app/assets/images/move.png b/app/assets/images/move.png index 9d2d55ddf0b460d8effa80bca82340f5a6ca3341..6a0567f8f2534837e7280dd41e4bf4b98725a3bf 100644 Binary files a/app/assets/images/move.png and b/app/assets/images/move.png differ diff --git a/app/assets/images/no_avatar.png b/app/assets/images/no_avatar.png index dac3ab1bb890ee2b583ac5eaa060d972b6e35867..8287acbce13e32d0823c8f5fd449099c1c61d6cd 100644 Binary files a/app/assets/images/no_avatar.png and b/app/assets/images/no_avatar.png differ diff --git a/app/assets/images/no_group_avatar.png b/app/assets/images/no_group_avatar.png index a97d4515982bce6cb4973a8433b9ae0363e43d46..bfb31bb2184f6895c55c3027ddf9e357170bc338 100644 Binary files a/app/assets/images/no_group_avatar.png and b/app/assets/images/no_group_avatar.png differ diff --git a/app/assets/images/slider_handles.png b/app/assets/images/slider_handles.png index a6d477033fa5436fbad5409adcb549ac8d8f01e0..884378ec96a20c1cb2a419e1dc1aa68ab9d85c15 100644 Binary files a/app/assets/images/slider_handles.png and b/app/assets/images/slider_handles.png differ diff --git a/app/assets/images/solarized-dark-scheme-preview.png b/app/assets/images/solarized-dark-scheme-preview.png index ae092ab52139e10183efd5c175768a8e355f6678..7ed7336896b1daa3d06b3f304931bc2006a3b145 100644 Binary files a/app/assets/images/solarized-dark-scheme-preview.png and b/app/assets/images/solarized-dark-scheme-preview.png differ diff --git a/app/assets/images/solarized-light-scheme-preview.png b/app/assets/images/solarized-light-scheme-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..c50db75449b974294a612f3215a673c7c2217f80 Binary files /dev/null and b/app/assets/images/solarized-light-scheme-preview.png differ diff --git a/app/assets/images/switch_icon.png b/app/assets/images/switch_icon.png index 6b8bde41bc95c15df2f7ce6d18330cce1181a76c..c6b6c8d9521f64b00990ca5352c8ce269e9a3e4a 100644 Binary files a/app/assets/images/switch_icon.png and b/app/assets/images/switch_icon.png differ diff --git a/app/assets/images/trans_bg.gif b/app/assets/images/trans_bg.gif index 5f6ed04a43c97debddc5c6be5697ba74d0504adf..1a1c9c15ec71a58db869578399068cf313c51599 100644 Binary files a/app/assets/images/trans_bg.gif and b/app/assets/images/trans_bg.gif differ diff --git a/app/assets/images/white-scheme-preview.png b/app/assets/images/white-scheme-preview.png index d1866e0015803d0180553d350dffa322b5d9f958..fc4c40b9227cced4692d9c3f0b3e09d62894fe3e 100644 Binary files a/app/assets/images/white-scheme-preview.png and b/app/assets/images/white-scheme-preview.png differ diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee index 4f76d8ce48603ab849a4147a47e139d2a2238a22..777c62dc1b7773254cab7bfbe588166832a944dc 100644 --- a/app/assets/javascripts/activities.js.coffee +++ b/app/assets/javascripts/activities.js.coffee @@ -12,7 +12,7 @@ class @Activities toggleFilter: (sender) -> - sender.parent().toggleClass "inactive" + sender.parent().toggleClass "active" event_filters = $.cookie("event_filter") filter = sender.attr("id").split("_")[0] if event_filters diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index fafa5cdfaa4c83d329af632c1269a605568c2c1d..9e5d594c861d6867308d9c47b571795fe5f51efd 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -1,61 +1,24 @@ @Api = - users_path: "/api/:version/users.json" - user_path: "/api/:version/users/:id.json" - notes_path: "/api/:version/projects/:id/notes.json" + groups_path: "/api/:version/groups.json" + group_path: "/api/:version/groups/:id.json" namespaces_path: "/api/:version/namespaces.json" - project_users_path: "/api/:version/projects/:id/users.json" - # Get 20 (depends on api) recent notes - # and sort the ascending from oldest to newest - notes: (project_id, callback) -> - url = Api.buildUrl(Api.notes_path) - url = url.replace(':id', project_id) - - $.ajax( - url: url, - data: - private_token: gon.api_token - gfm: true - recent: true - dataType: "json" - ).done (notes) -> - notes.sort (a, b) -> - return a.id - b.id - callback(notes) - - user: (user_id, callback) -> - url = Api.buildUrl(Api.user_path) - url = url.replace(':id', user_id) + group: (group_id, callback) -> + url = Api.buildUrl(Api.group_path) + url = url.replace(':id', group_id) $.ajax( url: url data: private_token: gon.api_token dataType: "json" - ).done (user) -> - callback(user) - - # Return users list. Filtered by query - # Only active users retrieved - users: (query, callback) -> - url = Api.buildUrl(Api.users_path) - - $.ajax( - url: url - data: - private_token: gon.api_token - search: query - per_page: 20 - active: true - dataType: "json" - ).done (users) -> - callback(users) + ).done (group) -> + callback(group) - # Return project users list. Filtered by query - # Only active users retrieved - projectUsers: (project_id, query, callback) -> - url = Api.buildUrl(Api.project_users_path) - url = url.replace(':id', project_id) + # Return groups list. Filtered by query + # Only active groups retrieved + groups: (query, skip_ldap, callback) -> + url = Api.buildUrl(Api.groups_path) $.ajax( url: url @@ -63,10 +26,9 @@ private_token: gon.api_token search: query per_page: 20 - active: true dataType: "json" - ).done (users) -> - callback(users) + ).done (groups) -> + callback(groups) # Return namespaces list. Filtered by query namespaces: (query, callback) -> diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index e9a28c1215933da2a27689ea056e9b4c3fd41559..fda142293bc9b42daab5866d9c52f38339a8409e 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -16,16 +16,16 @@ #= require jquery.scrollTo #= require jquery.blockUI #= require jquery.turbolinks +#= require jquery.sticky-kit.min #= require turbolinks +#= require autosave #= require bootstrap -#= require password_strength #= require select2 #= require raphael #= require g.raphael-min #= require g.bar-min #= require chart-lib.min #= require branch-graph -#= require highlight.pack #= require ace/ace #= require ace/ext-searchbox #= require d3 @@ -33,7 +33,6 @@ #= require nprogress #= require nprogress-turbolinks #= require dropzone -#= require semantic-ui/sidebar #= require mousetrap #= require mousetrap/pause #= require shortcuts @@ -41,6 +40,7 @@ #= require shortcuts_dashboard_navigation #= require shortcuts_issueable #= require shortcuts_network +#= require cal-heatmap #= require_tree . window.slugify = (text) -> @@ -51,12 +51,6 @@ window.ajaxGet = (url) -> window.showAndHide = (selector) -> -window.errorMessage = (message) -> - ehtml = $("
") - ehtml.addClass("error_message") - ehtml.html(message) - ehtml - window.split = (val) -> return val.split( /,\s*/ ) @@ -82,24 +76,18 @@ window.disableButtonIfEmptyField = (field_selector, button_selector) -> # Disable button if any input field with given selector is empty window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) -> closest_submit = form.find(button_selector) - empty = false - form.find('input').filter(form_selector).each -> - empty = true if rstrip($(this).val()) is "" - - if empty - closest_submit.disable() - else - closest_submit.enable() - - form.keyup -> - empty = false + updateButtons = -> + filled = true form.find('input').filter(form_selector).each -> - empty = true if rstrip($(this).val()) is "" + filled = rstrip($(this).val()) != "" || !$(this).attr('required') - if empty - closest_submit.disable() - else + if filled closest_submit.enable() + else + closest_submit.disable() + + updateButtons() + form.keyup(updateButtons) window.sanitize = (str) -> return str.replace(/<(?:.|\n)*?>/gm, '') @@ -115,8 +103,17 @@ window.unbindEvents = -> $(document).unbind('scroll') $(document).off('scroll') +window.shiftWindow = -> + scrollBy 0, -50 + document.addEventListener("page:fetch", unbindEvents) +# Scroll the window to avoid the topnav bar +# https://github.com/twitter/bootstrap/issues/1768 +if location.hash + setTimeout shiftWindow, 1 +window.addEventListener "hashchange", shiftWindow + $ -> # Click a .one_click_select field, select the contents $(".one_click_select").on 'click', -> $(@).select() @@ -172,9 +169,7 @@ $ -> # Show/hide comments on diff $("body").on "click", ".js-toggle-diff-comments", (e) -> - $(@).find('i'). - toggleClass('fa fa-chevron-down'). - toggleClass('fa fa-chevron-up') + $(@).toggleClass('active') $(@).closest(".diff-file").find(".notes_holder").toggle() e.preventDefault() @@ -185,6 +180,8 @@ $ -> form = btn.closest("form") new ConfirmDangerModal(form, text) + new Aside() + (($) -> # Disable an element and add the 'disabled' Bootstrap class $.fn.extend disable: -> diff --git a/app/assets/javascripts/aside.js.coffee b/app/assets/javascripts/aside.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..854731019443e3ea1d49e73a3fa5a12e5fc00185 --- /dev/null +++ b/app/assets/javascripts/aside.js.coffee @@ -0,0 +1,17 @@ +class @Aside + constructor: -> + $(document).off "click", "a.show-aside" + $(document).on "click", 'a.show-aside', (e) -> + e.preventDefault() + btn = $(e.currentTarget) + icon = btn.find('i') + console.log('1') + + if icon.hasClass('fa-angle-left') + btn.parent().find('section').hide() + btn.parent().find('aside').fadeIn() + icon.removeClass('fa-angle-left').addClass('fa-angle-right') + else + btn.parent().find('aside').hide() + btn.parent().find('section').fadeIn() + icon.removeClass('fa-angle-right').addClass('fa-angle-left') diff --git a/app/assets/javascripts/autosave.js.coffee b/app/assets/javascripts/autosave.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..5d3fe81da74aefe071aaf29f905d29f39a898859 --- /dev/null +++ b/app/assets/javascripts/autosave.js.coffee @@ -0,0 +1,39 @@ +class @Autosave + constructor: (field, key) -> + @field = field + + key = key.join("/") if key.join? + @key = "autosave/#{key}" + + @field.data "autosave", this + + @restore() + + @field.on "input", => @save() + + restore: -> + return unless window.localStorage? + + try + text = window.localStorage.getItem @key + catch + return + + @field.val text if text?.length > 0 + @field.trigger "input" + + save: -> + return unless window.localStorage? + + text = @field.val() + if text?.length > 0 + try + window.localStorage.setItem @key, text + else + @reset() + + reset: -> + return unless window.localStorage? + + try + window.localStorage.removeItem @key diff --git a/app/assets/javascripts/behaviors/toggle_diff_line_wrap_behavior.coffee b/app/assets/javascripts/behaviors/toggle_diff_line_wrap_behavior.coffee deleted file mode 100644 index 691ed4f98aeaee5f8db47b0dea943507cd22655d..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/behaviors/toggle_diff_line_wrap_behavior.coffee +++ /dev/null @@ -1,14 +0,0 @@ -$ -> - # Toggle line wrapping in diff. - # - # %div.diff-file - # %input.js-toggle-diff-line-wrap - # %td.line_content - # - $("body").on "click", ".js-toggle-diff-line-wrap", (e) -> - diffFile = $(@).closest(".diff-file") - if $(@).is(":checked") - diffFile.addClass("diff-wrap-lines") - else - diffFile.removeClass("diff-wrap-lines") - diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob/blob.js.coffee similarity index 97% rename from app/assets/javascripts/blob.js.coffee rename to app/assets/javascripts/blob/blob.js.coffee index a5f15f80c5c66bfe41f8791a395dfd58826fb43a..37a175fdbc725fdaceb0187ab73547063c0821bb 100644 --- a/app/assets/javascripts/blob.js.coffee +++ b/app/assets/javascripts/blob/blob.js.coffee @@ -26,7 +26,7 @@ class @BlobView unless isNaN first_line $("#tree-content-holder .highlight .line").removeClass("hll") $("#LC#{line}").addClass("hll") for line in [first_line..last_line] - $.scrollTo("#L#{first_line}") unless e? + $.scrollTo("#L#{first_line}", offset: -50) unless e? # parse selected lines from hash # always return first and last line (initialized to NaN) diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..2e91a06daa8b900eda0d91f1721ce26a30d842be --- /dev/null +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -0,0 +1,44 @@ +class @EditBlob + constructor: (assets_path, mode)-> + ace.config.set "modePath", assets_path + '/ace' + ace.config.loadModule "ace/ext/searchbox" + if mode + ace_mode = mode + editor = ace.edit("editor") + editor.focus() + @editor = editor + + if ace_mode + editor.getSession().setMode "ace/mode/" + ace_mode + + disableButtonIfEmptyField "#commit_message", ".js-commit-button" + $(".js-commit-button").click -> + $("#file-content").val editor.getValue() + $(".file-editor form").submit() + return false + + editModePanes = $(".js-edit-mode-pane") + editModeLinks = $(".js-edit-mode a") + editModeLinks.click (event) -> + event.preventDefault() + currentLink = $(this) + paneId = currentLink.attr("href") + currentPane = editModePanes.filter(paneId) + editModeLinks.parent().removeClass "active hover" + currentLink.parent().addClass "active hover" + editModePanes.hide() + if paneId is "#preview" + currentPane.fadeIn 200 + $.post currentLink.data("preview-url"), + content: editor.getValue() + , (response) -> + currentPane.empty().append response + return + + else + currentPane.fadeIn 200 + editor.focus() + return + + editor: -> + return @editor diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..ab8f98715e86edf03dbc0d142e079958ae50d756 --- /dev/null +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -0,0 +1,21 @@ +class @NewBlob + constructor: (assets_path, mode)-> + ace.config.set "modePath", assets_path + '/ace' + ace.config.loadModule "ace/ext/searchbox" + if mode + ace_mode = mode + editor = ace.edit("editor") + editor.focus() + @editor = editor + + if ace_mode + editor.getSession().setMode "ace/mode/" + ace_mode + + disableButtonIfEmptyField "#commit_message", ".js-commit-button" + $(".js-commit-button").click -> + $("#file-content").val editor.getValue() + $(".file-editor form").submit() + return false + + editor: -> + return @editor diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..44d75bd694f009aa8db8915c26179f1be2626f30 --- /dev/null +++ b/app/assets/javascripts/calendar.js.coffee @@ -0,0 +1,38 @@ +class @Calendar + options = + month: "short" + day: "numeric" + year: "numeric" + + constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> + cal = new CalHeatMap() + cal.init + itemName: ["contribution"] + data: timestamps + start: new Date(starting_year, starting_month) + domainLabelFormat: "%b" + id: "cal-heatmap" + domain: "month" + subDomain: "day" + range: 12 + tooltip: true + label: + position: "top" + legend: [ + 0 + 10 + 20 + 30 + ] + legendCellPadding: 3 + onClick: (date, count) -> + formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() + $.ajax + url: calendar_activities_path + data: + date: formated_date + cache: false + dataType: "html" + success: (data) -> + $(".user-calendar-activities").html data + diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee index 6ef5a539b8fede86d194cf85ee8564a1242bc3ae..00ee503ff165c56ecafb3e63bd070cb465c8954d 100644 --- a/app/assets/javascripts/dashboard.js.coffee +++ b/app/assets/javascripts/dashboard.js.coffee @@ -1,30 +1,3 @@ class @Dashboard constructor: -> - @initSidebarTab() - - $(".dash-filter").keyup -> - terms = $(this).val() - uiBox = $(this).parents('.panel').first() - if terms == "" || terms == undefined - uiBox.find(".dash-list li").show() - else - uiBox.find(".dash-list li").each (index) -> - name = $(this).find(".filter-title").text() - - if name.toLowerCase().search(terms.toLowerCase()) == -1 - $(this).hide() - else - $(this).show() - - - - initSidebarTab: -> - key = "dashboard_sidebar_filter" - - # store selection in cookie - $('.dash-sidebar-tabs a').on 'click', (e) -> - $.cookie(key, $(e.target).attr('id')) - - # show tab from cookie - sidebar_filter = $.cookie(key) - $("#" + sidebar_filter).tab('show') if sidebar_filter + new ProjectsList() diff --git a/app/assets/javascripts/diff.js.coffee b/app/assets/javascripts/diff.js.coffee index 52b4208524ff80afabcc076fab555094418d1a31..069f91c30e1027dc4ccca0cf5d10ad9dc9727db7 100644 --- a/app/assets/javascripts/diff.js.coffee +++ b/app/assets/javascripts/diff.js.coffee @@ -1,6 +1,7 @@ class @Diff UNFOLD_COUNT = 20 constructor: -> + $(document).off('click', '.js-unfold') $(document).on('click', '.js-unfold', (event) => target = $(event.target) unfoldBottom = target.hasClass('js-unfold-bottom') diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index fb1adbc4b3d03a424de86d5ccd2feed9c393c0f5..821712f751211752ef6f2655b5b3db29e929abfc 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -4,7 +4,6 @@ $ -> class Dispatcher constructor: () -> @initSearch() - @initHighlight() @initPageScripts() initPageScripts: -> @@ -27,40 +26,61 @@ class Dispatcher new ZenMode() when 'projects:milestones:show' new Milestone() - when 'projects:milestones:new' + when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() + when 'projects:compare:show' + new Diff() when 'projects:issues:new','projects:issues:edit' GitLab.GfmAutoComplete.setup() shortcut_handler = new ShortcutsNavigation() new ZenMode() + new DropzoneInput($('.issue-form')) + if page == 'projects:issues:new' + new IssuableForm($('.issue-form')) when 'projects:merge_requests:new', 'projects:merge_requests:edit' GitLab.GfmAutoComplete.setup() new Diff() shortcut_handler = new ShortcutsNavigation() new ZenMode() + new DropzoneInput($('.merge-request-form')) + if page == 'projects:merge_requests:new' + new IssuableForm($('.merge-request-form')) when 'projects:merge_requests:show' new Diff() shortcut_handler = new ShortcutsIssueable() new ZenMode() when "projects:merge_requests:diffs" new Diff() + new ZenMode() when 'projects:merge_requests:index' shortcut_handler = new ShortcutsNavigation() + MergeRequests.init() when 'dashboard:show' new Dashboard() new Activities() + when 'dashboard:projects:starred' + new Activities() + new ProjectsList() when 'projects:commit:show' new Commit() new Diff() + new ZenMode() shortcut_handler = new ShortcutsNavigation() when 'projects:commits:show' shortcut_handler = new ShortcutsNavigation() - when 'groups:show', 'projects:show' + when 'projects:show' + new Activities() + shortcut_handler = new ShortcutsNavigation() + when 'groups:show' new Activities() shortcut_handler = new ShortcutsNavigation() - when 'groups:members' + new ProjectsList() + when 'groups:group_members:index' new GroupMembers() new UsersSelect() + when 'projects:project_members:index' + new ProjectMembers() + new UsersSelect() when 'groups:new', 'groups:edit', 'admin:groups:edit' new GroupAvatar() when 'projects:tree:show' @@ -75,8 +95,11 @@ class Dispatcher # Ensure we don't create a particular shortcut handler here. This is # already created, where the network graph is created. shortcut_handler = true + when 'projects:forks:new' + new ProjectFork() when 'users:show' new User() + new Activities() switch path.first() when 'admin' @@ -92,7 +115,10 @@ class Dispatcher new Profile() when 'projects' new Project() + new ProjectAvatar() switch path[1] + when 'compare' + shortcut_handler = new ShortcutsNavigation() when 'edit' shortcut_handler = new ShortcutsNavigation() new ProjectNew() @@ -101,16 +127,16 @@ class Dispatcher when 'show' new ProjectShow() when 'issues', 'merge_requests' - new ProjectUsersSelect() + new UsersSelect() when 'wikis' new Wikis() shortcut_handler = new ShortcutsNavigation() new ZenMode() + new DropzoneInput($('.wiki-form')) when 'snippets', 'labels', 'graphs' shortcut_handler = new ShortcutsNavigation() - when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' + when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' shortcut_handler = new ShortcutsNavigation() - new UsersSelect() # If we haven't installed a custom shortcut handler, install the default one @@ -124,10 +150,3 @@ class Dispatcher project_ref = opts.data('autocomplete-project-ref') new SearchAutocomplete(path, project_id, project_ref) - - initHighlight: -> - $('.highlight pre code').each (i, e) -> - $(e).html($.map($(e).html().split("\n"), (line, i) -> - "" + line + "" - ).join("\n")) - hljs.highlightBlock(e) diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..06e9f0001ae9064dbb5bb74a74267e0da98d8895 --- /dev/null +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -0,0 +1,240 @@ +class @DropzoneInput + constructor: (form) -> + Dropzone.autoDiscover = false + alertClass = "alert alert-danger alert-dismissable div-dropzone-alert" + alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\"" + divHover = "
" + divSpinner = "" + divAlert = "" + iconPaperclip = "" + iconSpinner = "" + btnAlert = "" + project_uploads_path = window.project_uploads_path or null + + form_textarea = $(form).find("textarea.markdown-area") + form_textarea.wrap "" + form_textarea.bind 'paste', (event) => + handlePaste(event) + + form_dropzone = $(form).find('.div-dropzone') + form_dropzone.parent().addClass "div-dropzone-wrapper" + form_dropzone.append divHover + $(".div-dropzone-hover").append iconPaperclip + form_dropzone.append divSpinner + $(".div-dropzone-spinner").append iconSpinner + $(".div-dropzone-spinner").css + "opacity": 0 + "display": "none" + + # Preview button + $(document).off "click", ".js-md-preview-button" + $(document).on "click", ".js-md-preview-button", (e) -> + ### + Shows the Markdown preview. + + Lets the server render GFM into Html and displays it. + ### + e.preventDefault() + form = $(this).closest("form") + # toggle tabs + form.find(".js-md-write-button").parent().removeClass "active" + form.find(".js-md-preview-button").parent().addClass "active" + + # toggle content + form.find(".md-write-holder").hide() + form.find(".md-preview-holder").show() + + preview = form.find(".js-md-preview") + mdText = form.find(".markdown-area").val() + if mdText.trim().length is 0 + preview.text "Nothing to preview." + else + preview.text "Loading..." + $.post($(this).data("url"), + md_text: mdText + ).success (previewData) -> + preview.html previewData + + # Write button + $(document).off "click", ".js-md-write-button" + $(document).on "click", ".js-md-write-button", (e) -> + ### + Shows the Markdown textarea. + ### + e.preventDefault() + form = $(this).closest("form") + # toggle tabs + form.find(".js-md-write-button").parent().addClass "active" + form.find(".js-md-preview-button").parent().removeClass "active" + + # toggle content + form.find(".md-write-holder").show() + form.find(".md-preview-holder").hide() + + dropzone = form_dropzone.dropzone( + url: project_uploads_path + dictDefaultMessage: "" + clickable: true + paramName: "file" + maxFilesize: 10 + uploadMultiple: false + headers: + "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") + + previewContainer: false + + processing: -> + $(".div-dropzone-alert").alert "close" + + dragover: -> + form_textarea.addClass "div-dropzone-focus" + form.find(".div-dropzone-hover").css "opacity", 0.7 + return + + dragleave: -> + form_textarea.removeClass "div-dropzone-focus" + form.find(".div-dropzone-hover").css "opacity", 0 + return + + drop: -> + form_textarea.removeClass "div-dropzone-focus" + form.find(".div-dropzone-hover").css "opacity", 0 + form_textarea.focus() + return + + success: (header, response) -> + child = $(dropzone[0]).children("textarea") + $(child).val $(child).val() + formatLink(response.link) + "\n" + return + + error: (temp, errorMessage) -> + checkIfMsgExists = $(".error-alert").children().length + if checkIfMsgExists is 0 + $(".error-alert").append divAlert + $(".div-dropzone-alert").append btnAlert + errorMessage + return + + sending: -> + form_dropzone.find(".div-dropzone-spinner").css + "opacity": 0.7 + "display": "inherit" + return + + complete: -> + $(".dz-preview").remove() + $(".markdown-area").trigger "input" + $(".div-dropzone-spinner").css + "opacity": 0 + "display": "none" + return + ) + + child = $(dropzone[0]).children("textarea") + + formatLink = (link) -> + text = "[#{link.alt}](#{link.url})" + text = "!#{text}" if link.is_image + text + + handlePaste = (event) -> + pasteEvent = event.originalEvent + if pasteEvent.clipboardData and pasteEvent.clipboardData.items + image = isImage(pasteEvent) + if image + event.preventDefault() + + filename = getFilename(pasteEvent) or "image.png" + text = "{{" + filename + "}}" + pasteText(text) + uploadFile image.getAsFile(), filename + + isImage = (data) -> + i = 0 + while i < data.clipboardData.items.length + item = data.clipboardData.items[i] + if item.type.indexOf("image") isnt -1 + return item + i++ + return false + + pasteText = (text) -> + caretStart = $(child)[0].selectionStart + caretEnd = $(child)[0].selectionEnd + textEnd = $(child).val().length + + beforeSelection = $(child).val().substring 0, caretStart + afterSelection = $(child).val().substring caretEnd, textEnd + $(child).val beforeSelection + text + afterSelection + form_textarea.trigger "input" + + getFilename = (e) -> + if window.clipboardData and window.clipboardData.getData + value = window.clipboardData.getData("Text") + else if e.clipboardData and e.clipboardData.getData + value = e.clipboardData.getData("text/plain") + + value = value.split("\r") + value.first() + + uploadFile = (item, filename) -> + formData = new FormData() + formData.append "file", item, filename + $.ajax + url: project_uploads_path + type: "POST" + data: formData + dataType: "json" + processData: false + contentType: false + headers: + "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") + + beforeSend: -> + showSpinner() + closeAlertMessage() + + success: (e, textStatus, response) -> + insertToTextArea(filename, formatLink(response.responseJSON.link)) + + error: (response) -> + showError(response.responseJSON.message) + + complete: -> + closeSpinner() + + insertToTextArea = (filename, url) -> + $(child).val (index, val) -> + val.replace("{{" + filename + "}}", url + "\n") + + appendToTextArea = (url) -> + $(child).val (index, val) -> + val + url + "\n" + + showSpinner = (e) -> + form.find(".div-dropzone-spinner").css + "opacity": 0.7 + "display": "inherit" + + closeSpinner = -> + form.find(".div-dropzone-spinner").css + "opacity": 0 + "display": "none" + + showError = (message) -> + checkIfMsgExists = $(".error-alert").children().length + if checkIfMsgExists is 0 + $(".error-alert").append divAlert + $(".div-dropzone-alert").append btnAlert + message + + closeAlertMessage = -> + form.find(".div-dropzone-alert").alert "close" + + form.find(".markdown-selector").click (e) -> + e.preventDefault() + $(@).closest('.gfm-form').find('.div-dropzone').click() + return + + formatLink: (link) -> + text = "[#{link.alt}](#{link.url})" + text = "!#{text}" if link.is_image + text \ No newline at end of file diff --git a/app/assets/javascripts/groups_select.js.coffee b/app/assets/javascripts/groups_select.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..1084e2a17d191db263d3a039f5746b85dfaf909e --- /dev/null +++ b/app/assets/javascripts/groups_select.js.coffee @@ -0,0 +1,41 @@ +class @GroupsSelect + constructor: -> + $('.ajax-groups-select').each (i, select) => + skip_ldap = $(select).hasClass('skip_ldap') + + $(select).select2 + placeholder: "Search for a group" + multiple: $(select).hasClass('multiselect') + minimumInputLength: 0 + query: (query) -> + Api.groups query.term, skip_ldap, (groups) -> + data = { results: groups } + query.callback(data) + + initSelection: (element, callback) -> + id = $(element).val() + if id isnt "" + Api.group(id, callback) + + + formatResult: (args...) => + @formatResult(args...) + formatSelection: (args...) => + @formatSelection(args...) + dropdownCssClass: "ajax-groups-dropdown" + escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results + m + + formatResult: (group) -> + if group.avatar_url + avatar = group.avatar_url + else + avatar = gon.default_avatar_url + + ")?\[(? You can confirm your account through the link below: <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %> <%= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) %> Someone has requested a link to change your password, and you can do this through the link below. <%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %> <%= link_to 'Change your password', edit_password_url(@resource, reset_password_token: @token) %> If you didn't request this, please ignore this email. Your password won't change until you access the link above and create a new one. Click the link below to unlock your account: <%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %> <%= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) %> Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>. Unhappy? <%= link_to "Cancel your account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>. This namespace already been taken! Please choose another one Access denied! Please verify you can add deploy keys to this repository.#{body}"
+ end
+
+ def create_issue_message(data)
+ user_name = data[:user][:name]
+
+ obj_attr = data[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ title = obj_attr[:title]
+ state = obj_attr[:state]
+ issue_iid = obj_attr[:iid]
+ issue_url = obj_attr[:url]
+ description = obj_attr[:description]
+
+ issue_link = "issue ##{issue_iid}"
+ message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}"
+
+ if description
+ description = format_body(description)
+ message << description
+ end
+
+ message
+ end
+
+ def create_merge_request_message(data)
+ user_name = data[:user][:name]
+
+ obj_attr = data[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ merge_request_id = obj_attr[:iid]
+ source_branch = obj_attr[:source_branch]
+ target_branch = obj_attr[:target_branch]
+ state = obj_attr[:state]
+ description = obj_attr[:description]
+ title = obj_attr[:title]
+
+ merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
+ merge_request_link = "merge request ##{merge_request_id}"
+ message = "#{user_name} #{state} #{merge_request_link} in " \
+ "#{project_link}: #{title}"
+
+ if description
+ description = format_body(description)
+ message << description
+ end
+
+ message
+ end
+
+ def format_title(title)
+ "" + title.lines.first.chomp + ""
+ end
+
+ def create_note_message(data)
+ data = HashWithIndifferentAccess.new(data)
+ user_name = data[:user][:name]
+
+ repo_attr = HashWithIndifferentAccess.new(data[:repository])
+
+ obj_attr = HashWithIndifferentAccess.new(data[:object_attributes])
+ note = obj_attr[:note]
+ note_url = obj_attr[:url]
+ noteable_type = obj_attr[:noteable_type]
+
+ case noteable_type
+ when "Commit"
+ commit_attr = HashWithIndifferentAccess.new(data[:commit])
+ subject_desc = commit_attr[:id]
+ subject_desc = Commit.truncate_sha(subject_desc)
+ subject_type = "commit"
+ title = format_title(commit_attr[:message])
+ when "Issue"
+ subj_attr = HashWithIndifferentAccess.new(data[:issue])
+ subject_id = subj_attr[:iid]
+ subject_desc = "##{subject_id}"
+ subject_type = "issue"
+ title = format_title(subj_attr[:title])
+ when "MergeRequest"
+ subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
+ subject_id = subj_attr[:iid]
+ subject_desc = "##{subject_id}"
+ subject_type = "merge request"
+ title = format_title(subj_attr[:title])
+ when "Snippet"
+ subj_attr = HashWithIndifferentAccess.new(data[:snippet])
+ subject_id = subj_attr[:id]
+ subject_desc = "##{subject_id}"
+ subject_type = "snippet"
+ title = format_title(subj_attr[:title])
+ end
+
+ subject_html = "#{subject_type} #{subject_desc}"
+ message = "#{user_name} commented on #{subject_html} in #{project_link}: "
+ message << title
+
+ if note
+ note = format_body(note)
+ message << note
+ end
+
+ message
+ end
+
+ def project_name
+ project.name_with_namespace.gsub(/\s/, '')
+ end
+
+ def project_url
+ project.web_url
+ end
+
+ def project_link
+ "#{project_name}"
+ end
+
+ def is_update?(data)
+ data[:object_attributes][:action] == 'update'
+ end
end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2bddb7b881cfd7f33dfa775d272d2d524f45603a
--- /dev/null
+++ b/app/models/project_services/irker_service.rb
@@ -0,0 +1,163 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'uri'
+
+class IrkerService < Service
+ prop_accessor :colorize_messages, :recipients, :channels
+ validates :recipients, presence: true, if: :activated?
+ validate :check_recipients_count, if: :activated?
+
+ before_validation :get_channels
+ after_initialize :initialize_settings
+
+ # Writer for RSpec tests
+ attr_writer :settings
+
+ def initialize_settings
+ # See the documentation (doc/project_services/irker.md) for possible values
+ # here
+ @settings ||= {
+ server_ip: 'localhost',
+ server_port: 6659,
+ max_channels: 3,
+ default_irc_uri: nil
+ }
+ end
+
+ def title
+ 'Irker (IRC gateway)'
+ end
+
+ def description
+ 'Send IRC messages, on update, to a list of recipients through an Irker '\
+ 'gateway.'
+ end
+
+ def help
+ msg = 'Recipients have to be specified with a full URI: '\
+ 'irc[s]://irc.network.net[:port]/#channel. Special cases: if you want '\
+ 'the channel to be a nickname instead, append ",isnick" to the channel '\
+ 'name; if the channel is protected by a secret password, append '\
+ '"?key=secretpassword" to the URI.'
+
+ unless @settings[:default_irc].nil?
+ msg += ' Note that a default IRC URI is provided by this service\'s '\
+ "administrator: #{default_irc}. You can thus just give a channel name."
+ end
+ msg
+ end
+
+ def to_param
+ 'irker'
+ end
+
+ def supported_events
+ %w(push)
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
+ IrkerWorker.perform_async(project_id, channels,
+ colorize_messages, data, @settings)
+ end
+
+ def fields
+ [
+ { type: 'textarea', name: 'recipients',
+ placeholder: 'Recipients/channels separated by whitespaces' },
+ { type: 'checkbox', name: 'colorize_messages' },
+ ]
+ end
+
+ private
+
+ def check_recipients_count
+ return true if recipients.nil? || recipients.empty?
+
+ if recipients.split(/\s+/).count > max_chans
+ errors.add(:recipients, "are limited to #{max_chans}")
+ end
+ end
+
+ def max_chans
+ @settings[:max_channels]
+ end
+
+ def get_channels
+ return true unless :activated?
+ return true if recipients.nil? || recipients.empty?
+
+ map_recipients
+
+ errors.add(:recipients, 'are all invalid') if channels.empty?
+ true
+ end
+
+ def map_recipients
+ self.channels = recipients.split(/\s+/).map do |recipient|
+ format_channel default_irc_uri, recipient
+ end
+ channels.reject! &:nil?
+ end
+
+ def default_irc_uri
+ default_irc = @settings[:default_irc_uri]
+ if !(default_irc.nil? || default_irc[-1] == '/')
+ default_irc += '/'
+ end
+ default_irc
+ end
+
+ def format_channel(default_irc, recipient)
+ cnt = 0
+ url = nil
+
+ # Try to parse the chan as a full URI
+ begin
+ uri = URI.parse(recipient)
+ raise URI::InvalidURIError if uri.scheme.nil? && cnt == 0
+ rescue URI::InvalidURIError
+ unless default_irc.nil?
+ cnt += 1
+ recipient = "#{default_irc}#{recipient}"
+ retry if cnt == 1
+ end
+ else
+ url = consider_uri uri
+ end
+ url
+ end
+
+ def consider_uri(uri)
+ # Authorize both irc://domain.com/#chan and irc://domain.com/chan
+ if uri.is_a?(URI) && uri.scheme[/^ircs?$/] && !uri.path.nil?
+ # Do not authorize irc://domain.com/
+ if uri.fragment.nil? && uri.path.length > 1
+ uri.to_s
+ else
+ # Authorize irc://domain.com/smthg#chan
+ # The irker daemon will deal with it by concatenating smthg and
+ # chan, thus sending messages on #smthgchan
+ uri.to_s
+ end
+ end
+ end
+end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8e90c44d10361925a1615e51b6b1c4c2abc6d882
--- /dev/null
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -0,0 +1,113 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+class IssueTrackerService < Service
+
+ validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
+
+ def category
+ :issue_tracker
+ end
+
+ def default?
+ false
+ end
+
+ def issue_url(iid)
+ self.issues_url.gsub(':id', iid.to_s)
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url' },
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url' }
+ ]
+ end
+
+ def initialize_properties
+ if properties.nil?
+ if enabled_in_gitlab_config
+ self.properties = {
+ title: issues_tracker['title'],
+ project_url: add_issues_tracker_id(issues_tracker['project_url']),
+ issues_url: add_issues_tracker_id(issues_tracker['issues_url']),
+ new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url'])
+ }
+ else
+ self.properties = {}
+ end
+ end
+ end
+
+ def supported_events
+ %w(push)
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
+ message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again."
+ result = false
+
+ begin
+ url = URI.parse(self.project_url)
+
+ if url.host && url.port
+ http = Net::HTTP.start(url.host, url.port, { open_timeout: 5, read_timeout: 5 })
+ response = http.head("/")
+
+ if response
+ message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
+ result = true
+ end
+ end
+ rescue Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error
+ message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
+ end
+ Rails.logger.info(message)
+ result
+ end
+
+ private
+
+ def enabled_in_gitlab_config
+ Gitlab.config.issues_tracker &&
+ Gitlab.config.issues_tracker.values.any? &&
+ issues_tracker
+ end
+
+ def issues_tracker
+ Gitlab.config.issues_tracker[to_param]
+ end
+
+ def add_issues_tracker_id(url)
+ if self.project
+ id = self.project.issues_tracker_id
+
+ if id
+ url = url.gsub(":issues_tracker_id", id)
+ end
+ end
+
+ url
+ end
+end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fcd9dc2f33674ccd1fbd781434c542e1dc3cc298
--- /dev/null
+++ b/app/models/project_services/jira_service.rb
@@ -0,0 +1,58 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+class JiraService < IssueTrackerService
+ include Rails.application.routes.url_helpers
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def help
+ issue_tracker_link = help_page_path("integration", "external-issue-tracker")
+
+ line1 = "Setting `project_url`, `issues_url` and `new_issue_url` will "\
+ "allow a user to easily navigate to the Jira issue tracker. "\
+ "See the [integration doc](#{issue_tracker_link}) for details."
+
+ line2 = 'Support for referencing commits and automatic closing of Jira issues directly ' \
+ 'from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html)'
+
+ [line1, line2].join("\n\n")
+ end
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'JIRA'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Jira issue tracker'
+ end
+ end
+
+ def to_param
+ 'jira'
+ end
+end
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index 09e114f9cca2fa139b9922da74195a362bdb4e74..ade9ee97873999daa81fba4880cd736b9d6319aa 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -2,14 +2,20 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
#
class PivotaltrackerService < Service
@@ -36,9 +42,15 @@ class PivotaltrackerService < Service
]
end
- def execute(push)
+ def supported_events
+ %w(push)
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
url = 'https://www.pivotaltracker.com/services/v5/source_commits'
- push[:commits].each do |commit|
+ data[:commits].each do |commit|
message = {
'source_commit' => {
'commit_id' => commit[:id],
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index f247fde776262e14db640e82eeae796de439d554..53edf522e9a729a5827911c85c04a8b049ad3d29 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -2,14 +2,20 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
#
class PushoverService < Service
@@ -75,21 +81,27 @@ class PushoverService < Service
]
end
- def execute(push_data)
- ref = push_data[:ref].gsub('refs/heads/', '')
- before = push_data[:before]
- after = push_data[:after]
+ def supported_events
+ %w(push)
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
+ ref = Gitlab::Git.ref_name(data[:ref])
+ before = data[:before]
+ after = data[:after]
- if before =~ /000000/
- message = "#{push_data[:user_name]} pushed new branch \"#{ref}\"."
- elsif after =~ /000000/
- message = "#{push_data[:user_name]} deleted branch \"#{ref}\"."
+ if Gitlab::Git.blank_ref?(before)
+ message = "#{data[:user_name]} pushed new branch \"#{ref}\"."
+ elsif Gitlab::Git.blank_ref?(after)
+ message = "#{data[:user_name]} deleted branch \"#{ref}\"."
else
- message = "#{push_data[:user_name]} push to branch \"#{ref}\"."
+ message = "#{data[:user_name]} push to branch \"#{ref}\"."
end
- if push_data[:total_commits_count] > 0
- message << "\nTotal commits count: #{push_data[:total_commits_count]}"
+ if data[:total_commits_count] > 0
+ message << "\nTotal commits count: #{data[:total_commits_count]}"
end
pushover_data = {
@@ -99,7 +111,7 @@ class PushoverService < Service
priority: priority,
title: "#{project.name_with_namespace}",
message: message,
- url: push_data[:repository][:homepage],
+ url: data[:repository][:homepage],
url_title: "See project #{project.name_with_namespace}"
}
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dd9ba97ee1fac487d66f5315955002abef9da6d9
--- /dev/null
+++ b/app/models/project_services/redmine_service.rb
@@ -0,0 +1,44 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+class RedmineService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Redmine'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Redmine issue tracker'
+ end
+ end
+
+ def to_param
+ 'redmine'
+ end
+end
diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb
deleted file mode 100644
index 28204e5ea60e6f2694296a9004df287449a459e1..0000000000000000000000000000000000000000
--- a/app/models/project_services/slack_message.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'slack-notifier'
-
-class SlackMessage
- def initialize(params)
- @after = params.fetch(:after)
- @before = params.fetch(:before)
- @commits = params.fetch(:commits, [])
- @project_name = params.fetch(:project_name)
- @project_url = params.fetch(:project_url)
- @ref = params.fetch(:ref).gsub('refs/heads/', '')
- @username = params.fetch(:user_name)
- end
-
- def pretext
- format(message)
- end
-
- def attachments
- return [] if new_branch? || removed_branch?
-
- commit_message_attachments
- end
-
- private
-
- attr_reader :after
- attr_reader :before
- attr_reader :commits
- attr_reader :project_name
- attr_reader :project_url
- attr_reader :ref
- attr_reader :username
-
- def message
- if new_branch?
- new_branch_message
- elsif removed_branch?
- removed_branch_message
- else
- push_message
- end
- end
-
- def format(string)
- Slack::Notifier::LinkFormatter.format(string)
- end
-
- def new_branch_message
- "#{username} pushed new branch #{branch_link} to #{project_link}"
- end
-
- def removed_branch_message
- "#{username} removed branch #{ref} from #{project_link}"
- end
-
- def push_message
- "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
- end
-
- def commit_messages
- commits.each_with_object('') do |commit, str|
- str << compose_commit_message(commit)
- end.chomp
- end
-
- def commit_message_attachments
- [{ text: format(commit_messages), color: attachment_color }]
- end
-
- def compose_commit_message(commit)
- author = commit.fetch(:author).fetch(:name)
- id = commit.fetch(:id)[0..8]
- message = commit.fetch(:message)
- url = commit.fetch(:url)
-
- "[#{id}](#{url}): #{message} - #{author}\n"
- end
-
- def new_branch?
- before =~ /000000/
- end
-
- def removed_branch?
- after =~ /000000/
- end
-
- def branch_url
- "#{project_url}/commits/#{ref}"
- end
-
- def compare_url
- "#{project_url}/compare/#{before}...#{after}"
- end
-
- def branch_link
- "[#{ref}](#{branch_url})"
- end
-
- def project_link
- "[#{project_name}](#{project_url})"
- end
-
- def compare_link
- "[Compare changes](#{compare_url})"
- end
-
- def attachment_color
- '#345'
- end
-end
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 963f5440b6f87fef112f7bb7cf5ad7ce2b1740bf..36d9874edd32d3b8d8a3f22c377458817858b935 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -2,18 +2,24 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
#
class SlackService < Service
- prop_accessor :webhook
+ prop_accessor :webhook, :username, :channel
validates :webhook, presence: true, if: :activated?
def title
@@ -30,20 +36,52 @@ class SlackService < Service
def fields
[
- { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }
+ { type: 'text', name: 'webhook',
+ placeholder: 'https://hooks.slack.com/services/...' },
+ { type: 'text', name: 'username', placeholder: 'username' },
+ { type: 'text', name: 'channel', placeholder: '#channel' }
]
end
- def execute(push_data)
+ def supported_events
+ %w(push issue merge_request note tag_push)
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
return unless webhook.present?
- message = SlackMessage.new(push_data.merge(
+ object_kind = data[:object_kind]
+
+ data = data.merge(
project_url: project_url,
project_name: project_name
- ))
+ )
+
+ # WebHook events often have an 'update' event that follows a 'open' or
+ # 'close' action. Ignore update events for now to prevent duplicate
+ # messages from arriving.
+
+ message = \
+ case object_kind
+ when "push", "tag_push"
+ PushMessage.new(data)
+ when "issue"
+ IssueMessage.new(data) unless is_update?(data)
+ when "merge_request"
+ MergeMessage.new(data) unless is_update?(data)
+ when "note"
+ NoteMessage.new(data)
+ end
- notifier = Slack::Notifier.new(webhook)
- notifier.ping(message.pretext, attachments: message.attachments)
+ opt = {}
+ opt[:channel] = channel if channel
+ opt[:username] = username if username
+
+ if message
+ notifier = Slack::Notifier.new(webhook, opt)
+ notifier.ping(message.pretext, attachments: message.attachments)
+ end
end
private
@@ -55,4 +93,13 @@ class SlackService < Service
def project_url
project.web_url
end
+
+ def is_update?(data)
+ data[:object_attributes][:action] == 'update'
+ end
end
+
+require "slack_service/issue_message"
+require "slack_service/push_message"
+require "slack_service/merge_message"
+require "slack_service/note_message"
diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/slack_service/base_message.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aa00d6061a118473758411a0b7581836ff9db291
--- /dev/null
+++ b/app/models/project_services/slack_service/base_message.rb
@@ -0,0 +1,31 @@
+require 'slack-notifier'
+
+class SlackService
+ class BaseMessage
+ def initialize(params)
+ raise NotImplementedError
+ end
+
+ def pretext
+ format(message)
+ end
+
+ def attachments
+ raise NotImplementedError
+ end
+
+ private
+
+ def message
+ raise NotImplementedError
+ end
+
+ def format(string)
+ Slack::Notifier::LinkFormatter.format(string)
+ end
+
+ def attachment_color
+ '#345'
+ end
+ end
+end
diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/slack_service/issue_message.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5af24a80609352547ab9b5b29549f6cd463a3a07
--- /dev/null
+++ b/app/models/project_services/slack_service/issue_message.rb
@@ -0,0 +1,56 @@
+class SlackService
+ class IssueMessage < BaseMessage
+ attr_reader :user_name
+ attr_reader :title
+ attr_reader :project_name
+ attr_reader :project_url
+ attr_reader :issue_iid
+ attr_reader :issue_url
+ attr_reader :action
+ attr_reader :state
+ attr_reader :description
+
+ def initialize(params)
+ @user_name = params[:user][:name]
+ @project_name = params[:project_name]
+ @project_url = params[:project_url]
+
+ obj_attr = params[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ @title = obj_attr[:title]
+ @issue_iid = obj_attr[:iid]
+ @issue_url = obj_attr[:url]
+ @action = obj_attr[:action]
+ @state = obj_attr[:state]
+ @description = obj_attr[:description]
+ end
+
+ def attachments
+ return [] unless opened_issue?
+
+ description_message
+ end
+
+ private
+
+ def message
+ "#{user_name} #{state} #{issue_link} in #{project_link}: *#{title}*"
+ end
+
+ def opened_issue?
+ action == "open"
+ end
+
+ def description_message
+ [{ text: format(description), color: attachment_color }]
+ end
+
+ def project_link
+ "[#{project_name}](#{project_url})"
+ end
+
+ def issue_link
+ "[issue ##{issue_iid}](#{issue_url})"
+ end
+ end
+end
diff --git a/app/models/project_services/slack_service/merge_message.rb b/app/models/project_services/slack_service/merge_message.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e792c258f7394a5e6e1c12dd222c75cd93d39620
--- /dev/null
+++ b/app/models/project_services/slack_service/merge_message.rb
@@ -0,0 +1,60 @@
+class SlackService
+ class MergeMessage < BaseMessage
+ attr_reader :user_name
+ attr_reader :project_name
+ attr_reader :project_url
+ attr_reader :merge_request_id
+ attr_reader :source_branch
+ attr_reader :target_branch
+ attr_reader :state
+ attr_reader :title
+
+ def initialize(params)
+ @user_name = params[:user][:name]
+ @project_name = params[:project_name]
+ @project_url = params[:project_url]
+
+ obj_attr = params[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ @merge_request_id = obj_attr[:iid]
+ @source_branch = obj_attr[:source_branch]
+ @target_branch = obj_attr[:target_branch]
+ @state = obj_attr[:state]
+ @title = format_title(obj_attr[:title])
+ end
+
+ def pretext
+ format(message)
+ end
+
+ def attachments
+ []
+ end
+
+ private
+
+ def format_title(title)
+ '*' + title.lines.first.chomp + '*'
+ end
+
+ def message
+ merge_request_message
+ end
+
+ def project_link
+ "[#{project_name}](#{project_url})"
+ end
+
+ def merge_request_message
+ "#{user_name} #{state} #{merge_request_link} in #{project_link}: #{title}"
+ end
+
+ def merge_request_link
+ "[merge request ##{merge_request_id}](#{merge_request_url})"
+ end
+
+ def merge_request_url
+ "#{project_url}/merge_requests/#{merge_request_id}"
+ end
+ end
+end
diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb
new file mode 100644
index 0000000000000000000000000000000000000000..074478b292da5ef73cd6cd7860bf0893404d1cfa
--- /dev/null
+++ b/app/models/project_services/slack_service/note_message.rb
@@ -0,0 +1,82 @@
+class SlackService
+ class NoteMessage < BaseMessage
+ attr_reader :message
+ attr_reader :user_name
+ attr_reader :project_name
+ attr_reader :project_link
+ attr_reader :note
+ attr_reader :note_url
+ attr_reader :title
+
+ def initialize(params)
+ params = HashWithIndifferentAccess.new(params)
+ @user_name = params[:user][:name]
+ @project_name = params[:project_name]
+ @project_url = params[:project_url]
+
+ obj_attr = params[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ @note = obj_attr[:note]
+ @note_url = obj_attr[:url]
+ noteable_type = obj_attr[:noteable_type]
+
+ case noteable_type
+ when "Commit"
+ create_commit_note(HashWithIndifferentAccess.new(params[:commit]))
+ when "Issue"
+ create_issue_note(HashWithIndifferentAccess.new(params[:issue]))
+ when "MergeRequest"
+ create_merge_note(HashWithIndifferentAccess.new(params[:merge_request]))
+ when "Snippet"
+ create_snippet_note(HashWithIndifferentAccess.new(params[:snippet]))
+ end
+ end
+
+ def attachments
+ description_message
+ end
+
+ private
+
+ def format_title(title)
+ title.lines.first.chomp
+ end
+
+ def create_commit_note(commit)
+ commit_sha = commit[:id]
+ commit_sha = Commit.truncate_sha(commit_sha)
+ commit_link = "[commit #{commit_sha}](#{@note_url})"
+ title = format_title(commit[:message])
+ @message = "#{@user_name} commented on #{commit_link} in #{project_link}: *#{title}*"
+ end
+
+ def create_issue_note(issue)
+ issue_iid = issue[:iid]
+ note_link = "[issue ##{issue_iid}](#{@note_url})"
+ title = format_title(issue[:title])
+ @message = "#{@user_name} commented on #{note_link} in #{project_link}: *#{title}*"
+ end
+
+ def create_merge_note(merge_request)
+ merge_request_id = merge_request[:iid]
+ merge_request_link = "[merge request ##{merge_request_id}](#{@note_url})"
+ title = format_title(merge_request[:title])
+ @message = "#{@user_name} commented on #{merge_request_link} in #{project_link}: *#{title}*"
+ end
+
+ def create_snippet_note(snippet)
+ snippet_id = snippet[:id]
+ snippet_link = "[snippet ##{snippet_id}](#{@note_url})"
+ title = format_title(snippet[:title])
+ @message = "#{@user_name} commented on #{snippet_link} in #{project_link}: *#{title}*"
+ end
+
+ def description_message
+ [{ text: format(@note), color: attachment_color }]
+ end
+
+ def project_link
+ "[#{@project_name}](#{@project_url})"
+ end
+ end
+end
diff --git a/app/models/project_services/slack_service/push_message.rb b/app/models/project_services/slack_service/push_message.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b26f3e9ddce9f392d6cc06e9446fb03a49fa1de3
--- /dev/null
+++ b/app/models/project_services/slack_service/push_message.rb
@@ -0,0 +1,110 @@
+class SlackService
+ class PushMessage < BaseMessage
+ attr_reader :after
+ attr_reader :before
+ attr_reader :commits
+ attr_reader :project_name
+ attr_reader :project_url
+ attr_reader :ref
+ attr_reader :ref_type
+ attr_reader :user_name
+
+ def initialize(params)
+ @after = params[:after]
+ @before = params[:before]
+ @commits = params.fetch(:commits, [])
+ @project_name = params[:project_name]
+ @project_url = params[:project_url]
+ @ref_type = Gitlab::Git.tag_ref?(params[:ref]) ? 'tag' : 'branch'
+ @ref = Gitlab::Git.ref_name(params[:ref])
+ @user_name = params[:user_name]
+ end
+
+ def pretext
+ format(message)
+ end
+
+ def attachments
+ return [] if new_branch? || removed_branch?
+
+ commit_message_attachments
+ end
+
+ private
+
+ def message
+ if new_branch?
+ new_branch_message
+ elsif removed_branch?
+ removed_branch_message
+ else
+ push_message
+ end
+ end
+
+ def format(string)
+ Slack::Notifier::LinkFormatter.format(string)
+ end
+
+ def new_branch_message
+ "#{user_name} pushed new #{ref_type} #{branch_link} to #{project_link}"
+ end
+
+ def removed_branch_message
+ "#{user_name} removed #{ref_type} #{ref} from #{project_link}"
+ end
+
+ def push_message
+ "#{user_name} pushed to #{ref_type} #{branch_link} of #{project_link} (#{compare_link})"
+ end
+
+ def commit_messages
+ commits.map { |commit| compose_commit_message(commit) }.join("\n")
+ end
+
+ def commit_message_attachments
+ [{ text: format(commit_messages), color: attachment_color }]
+ end
+
+ def compose_commit_message(commit)
+ author = commit[:author][:name]
+ id = Commit.truncate_sha(commit[:id])
+ message = commit[:message]
+ url = commit[:url]
+
+ "[#{id}](#{url}): #{message} - #{author}"
+ end
+
+ def new_branch?
+ Gitlab::Git.blank_ref?(before)
+ end
+
+ def removed_branch?
+ Gitlab::Git.blank_ref?(after)
+ end
+
+ def branch_url
+ "#{project_url}/commits/#{ref}"
+ end
+
+ def compare_url
+ "#{project_url}/compare/#{before}...#{after}"
+ end
+
+ def branch_link
+ "[#{ref}](#{branch_url})"
+ end
+
+ def project_link
+ "[#{project_name}](#{project_url})"
+ end
+
+ def compare_link
+ "[Compare changes](#{compare_url})"
+ end
+
+ def attachment_color
+ '#345'
+ end
+ end
+end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c26bc551352c3f2136c111795dcbe158635f2aed
--- /dev/null
+++ b/app/models/project_services/teamcity_service.rb
@@ -0,0 +1,145 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+class TeamcityService < CiService
+ include HTTParty
+
+ prop_accessor :teamcity_url, :build_type, :username, :password
+
+ validates :teamcity_url,
+ presence: true,
+ format: { with: URI::regexp }, if: :activated?
+ validates :build_type, presence: true, if: :activated?
+ validates :username,
+ presence: true,
+ if: ->(service) { service.password? }, if: :activated?
+ validates :password,
+ presence: true,
+ if: ->(service) { service.username? }, if: :activated?
+
+ attr_accessor :response
+
+ after_save :compose_service_hook, if: :activated?
+
+ def compose_service_hook
+ hook = service_hook || build_service_hook
+ hook.save
+ end
+
+ def title
+ 'JetBrains TeamCity CI'
+ end
+
+ def description
+ 'A continuous integration and build server'
+ end
+
+ def help
+ 'The build configuration in Teamcity must use the build format '\
+ 'number %build.vcs.number% '\
+ 'you will also want to configure monitoring of all branches so merge '\
+ 'requests build, that setting is in the vsc root advanced settings.'
+ end
+
+ def to_param
+ 'teamcity'
+ end
+
+ def supported_events
+ %w(push)
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'teamcity_url',
+ placeholder: 'TeamCity root URL like https://teamcity.example.com' },
+ { type: 'text', name: 'build_type',
+ placeholder: 'Build configuration ID' },
+ { type: 'text', name: 'username',
+ placeholder: 'A user with permissions to trigger a manual build' },
+ { type: 'password', name: 'password' },
+ ]
+ end
+
+ def build_info(sha)
+ url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
+ "branch:unspecified:any,number:#{sha}")
+ auth = {
+ username: username,
+ password: password,
+ }
+ @response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
+ end
+
+ def build_page(sha, ref)
+ build_info(sha) if @response.nil? || !@response.code
+
+ if @response.code != 200
+ # If actual build link can't be determined,
+ # send user to build summary page.
+ "#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
+ else
+ # If actual build link is available, go to build result page.
+ built_id = @response['build']['id']
+ "#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
+ "&buildTypeId=#{build_type}"
+ end
+ end
+
+ def commit_status(sha, ref)
+ build_info(sha) if @response.nil? || !@response.code
+ return :error unless @response.code == 200 || @response.code == 404
+
+ status = if @response.code == 404
+ 'Pending'
+ else
+ @response['build']['status']
+ end
+
+ if status.include?('SUCCESS')
+ 'success'
+ elsif status.include?('FAILURE')
+ 'failed'
+ elsif status.include?('Pending')
+ 'pending'
+ else
+ :error
+ end
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
+ auth = {
+ username: username,
+ password: password,
+ }
+
+ branch = Gitlab::Git.ref_name(data[:ref])
+
+ self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
+ body: "Cancel my account
+Cancel your account
-https://username:password@gitlab.company.com/group/project.git.
+ %li
+ The import will time out after 4 minutes. For big repositories, use a clone/push combination.
+ %li
+ To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}.
+
+ %hr.prepend-botton-10
.form-group
= f.label :description, class: 'control-label' do
@@ -69,7 +98,7 @@
.pull-right
.light
Need a group for several dependent projects?
- = link_to new_group_path, class: "btn btn-tiny" do
+ = link_to new_group_path, class: "btn btn-xs" do
Create a group
.save-project-loader.hide
@@ -78,3 +107,10 @@
%i.fa.fa-spinner.fa-spin
Creating project & repository.
%p Please wait a moment, this page will automatically refresh when ready.
+
+:coffeescript
+ $('.how_to_import_link').bind 'click', (e) ->
+ e.preventDefault()
+ import_modal = $(this).next(".modal").show()
+ $('.modal-header .close').bind 'click', ->
+ $(".modal").hide()
diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml
deleted file mode 100644
index f09d3659774510cb2a5dd0bc90c0ab775e12b9b7..0000000000000000000000000000000000000000
--- a/app/views/projects/new_tree/show.html.haml
+++ /dev/null
@@ -1,43 +0,0 @@
-%h3.page-title New file
-%hr
-.file-editor
- = form_tag(project_new_tree_path(@project, @id), method: :put, class: 'form-horizontal form-new-file') do
- .form-group.commit_message-group
- = label_tag 'file_name', class: 'control-label' do
- File name
- .col-sm-10
- .input-group
- %span.input-group-addon
- = @path[-1] == "/" ? @path : @path + "/"
- = text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true, class: 'form-control'
- %span.input-group-addon
- on
- %span= @ref
-
- .form-group.commit_message-group
- = label_tag :encoding, class: "control-label" do
- Encoding
- .col-sm-10
- = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
- .file-holder
- .file-title
- %i.fa.fa-file
- .file-content.code
- %pre#editor= params[:content]
-
- = render 'shared/commit_message_container', params: params,
- placeholder: 'Add new file'
- = hidden_field_tag 'content', '', id: 'file-content'
- = render 'projects/commit_button', ref: @ref,
- cancel_path: project_tree_path(@project, @id)
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
- var editor = ace.edit("editor");
-
- disableButtonIfAnyEmptyField($('.form-new-file'), '.form-control', '.btn-create')
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..720957e8336a1cc4ec51c05fdc0dce7ef7e1eeda
--- /dev/null
+++ b/app/views/projects/no_repo.html.haml
@@ -0,0 +1,22 @@
+%h2
+ %i.fa.fa-warning
+ No repository
+
+%p.slead
+ The repository for this project does not exist.
+ %br
+ This means you can not push code until you create an empty repository or import existing one.
+%hr
+
+.no-repo-actions
+ = link_to namespace_project_repository_path(@project.namespace, @project), method: :post, class: 'btn btn-primary' do
+ Create empty bare repository
+
+ %strong.prepend-left-10.append-right-10 or
+
+ = link_to new_namespace_project_import_path(@project.namespace, @project), class: 'btn' do
+ Import repository
+
+- if can? current_user, :remove_project, @project
+ .prepend-top-20
+ = link_to 'Remove project', project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml
index f4c6fad2fed0c3e8a089620801743f10e49aa903..3561ca49f8179c2c30a375a00c0c9a513412ecda 100644
--- a/app/views/projects/notes/_discussion.html.haml
+++ b/app/views/projects/notes/_discussion.html.haml
@@ -2,7 +2,8 @@
.timeline-entry
.timeline-entry-inner
.timeline-icon
- = image_tag avatar_icon(note.author_email), class: "avatar s40"
+ = link_to user_path(note.author) do
+ = image_tag avatar_icon(note.author_email), class: "avatar s40"
.timeline-content
- if note.for_merge_request?
- if note.outdated?
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..acb3991d2946a523d3d8e8b88ce48e4f6cbef87a
--- /dev/null
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -0,0 +1,15 @@
+.note-edit-form
+ = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
+ = note_target_fields(note)
+ = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
+ = render 'projects/zen', f: f, attr: :note,
+ classes: 'note_text js-note-text'
+
+ .comment-hints.clearfix
+ .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
+ .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
+
+ .note-form-actions
+ .buttons
+ = f.submit 'Save Comment', class: "btn btn-primary btn-save btn-grouped js-comment-button"
+ = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
\ No newline at end of file
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index c68b3817e79601f8dd6c085bd5b246c59c4e6d59..be96c302143cba44cf1c97163d98128d74672b53 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -1,27 +1,18 @@
-= form_for [@project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
- = note_target_fields
+= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
+ = note_target_fields(@note)
= f.hidden_field :commit_id
= f.hidden_field :line_code
= f.hidden_field :noteable_id
= f.hidden_field :noteable_type
- %ul.nav.nav-tabs
- %li.active
- = link_to '#note-write-holder', class: 'js-note-write-button' do
- Write
- %li
- = link_to '#note-preview-holder', class: 'js-note-preview-button', data: { url: preview_project_notes_path(@project) } do
- Preview
- %div
- .note-write-holder
- = render 'projects/zen', f: f, attr: :note,
- classes: 'note_text js-note-text'
- .light.clearfix
- .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
- .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
+ = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
+ = render 'projects/zen', f: f, attr: :note,
+ classes: 'note_text js-note-text'
+
+ .comment-hints.clearfix
+ .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
+ .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
- .note-preview-holder.hide
- .js-note-preview
.note-form-actions
.buttons
@@ -29,13 +20,5 @@
= yield(:note_actions)
%a.btn.grouped.js-close-discussion-note-form Cancel
- .note-form-option
- %a.choose-btn.btn.js-choose-note-attachment-button
- %i.fa.fa-paperclip
- %span Choose File ...
-
- %span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hidden"
-
:javascript
- window.project_image_path_upload = "#{upload_image_project_path @project}";
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index a25c5e207fb174960f962f24c9692b99acacbe62..71bdf5c8f2a0ad1168792de3a5b02779928fc84b 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -1,7 +1,11 @@
-%li.timeline-entry{ id: dom_id(note), class: dom_class(note), data: { discussion: note.discussion_id } }
+%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } }
.timeline-entry-inner
.timeline-icon
- = image_tag avatar_icon(note.author_email), class: "avatar s40"
+ - if note.system
+ %span.fa.fa-circle
+ - else
+ = link_to user_path(note.author) do
+ = image_tag avatar_icon(note.author_email), class: "avatar s40"
.timeline-content
.note-header
.note-actions
@@ -14,56 +18,54 @@
%i.fa.fa-pencil-square-o
Edit
- = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do
+ = link_to namespace_project_note_path(@project.namespace, @project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do
%i.fa.fa-trash-o.cred
Remove
+ - if note.system
+ = link_to user_path(note.author) do
+ = image_tag avatar_icon(note.author_email), class: "avatar s16"
= link_to_member(@project, note.author, avatar: false)
+ %span.author-username
+ = '@' + note.author.username
%span.note-last-update
= note_timestamp(note)
- - if note.upvote?
- %span.vote.upvote.label.label-success
- %i.fa.fa-thumbs-up
- \+1
- - if note.downvote?
- %span.vote.downvote.label.label-danger
- %i.fa.fa-thumbs-down
- \-1
+ - if note.superceded?(@notes)
+ - if note.upvote?
+ %span.vote.upvote.label.label-gray.strikethrough
+ %i.fa.fa-thumbs-up
+ \+1
+ - if note.downvote?
+ %span.vote.downvote.label.label-gray.strikethrough
+ %i.fa.fa-thumbs-down
+ \-1
+ - else
+ - if note.upvote?
+ %span.vote.upvote.label.label-success
+ %i.fa.fa-thumbs-up
+ \+1
+ - if note.downvote?
+ %span.vote.downvote.label.label-danger
+ %i.fa.fa-thumbs-down
+ \-1
.note-body
.note-text
= preserve do
= markdown(note.note, {no_header_anchors: true})
-
- .note-edit-form
- = form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
- = f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on'
-
- .form-actions.clearfix
- = f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button"
-
- .note-form-option
- %a.choose-btn.btn.js-choose-note-attachment-button
- %i.fa.fa-paperclip
- %span Choose File ...
-
- %span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hidden"
-
- = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
-
+ = render 'projects/notes/edit_form', note: note
- if note.attachment.url
.note-attachment
- if note.attachment.image?
- = link_to note.attachment.secure_url, target: '_blank' do
- = image_tag note.attachment.secure_url, class: 'note-image-attach'
+ = link_to note.attachment.url, target: '_blank' do
+ = image_tag note.attachment.url, class: 'note-image-attach'
.attachment
- = link_to note.attachment.secure_url, target: "_blank" do
+ = link_to note.attachment.url, target: "_blank" do
%i.fa.fa-paperclip
= note.attachment_identifier
- = link_to delete_attachment_project_note_path(@project, note),
+ = link_to delete_attachment_namespace_project_note_path(@project.namespace, @project, note),
title: "Delete this attachment", method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: "danger js-note-attachment-delete" do
%i.fa.fa-trash-o.cred
.clear
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 04ee17a40a0e6a17dd5904ffdea05c89afcce052..813e37276bdd8ec5fda2bad1f678a64069bedb25 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -7,4 +7,4 @@
= render "projects/notes/form"
:javascript
- new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
+ new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
diff --git a/app/views/projects/notes/discussions/_active.html.haml b/app/views/projects/notes/discussions/_active.html.haml
index 52c06ec172d879e3fd45c9a9f75af014113e6700..7c6f72431730a42361fa2225df776aaed3e4bf68 100644
--- a/app/views/projects/notes/discussions/_active.html.haml
+++ b/app/views/projects/notes/discussions/_active.html.haml
@@ -8,7 +8,7 @@
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion
- = link_to diffs_project_merge_request_path(note.project, note.noteable, anchor: note.line_code) do
+ = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
%strong on the diff
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml
index 94f16a5f02e78947fa6435100c4bafdf9ed23d3a..62609cfc1c8858d395a2e91b61c0921aaf0d7606 100644
--- a/app/views/projects/notes/discussions/_commit.html.haml
+++ b/app/views/projects/notes/discussions/_commit.html.haml
@@ -8,7 +8,7 @@
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion on commit
- = link_to(note.noteable.short_id, project_commit_path(note.project, note.noteable), class: 'monospace')
+ = link_to(note.noteable.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace')
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml
index b4d1cce7980dd866121f7653ee53a7d5e6904fa0..711aa39101b865a3c1008ba8b89c40b1d68f74e4 100644
--- a/app/views/projects/notes/discussions/_diff.html.haml
+++ b/app/views/projects/notes/discussions/_diff.html.haml
@@ -2,13 +2,13 @@
- if diff
.diff-file
.diff-header
- - if diff.deleted_file
- %span= diff.old_path
- - else
- %span= diff.new_path
- - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
- %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
- %br/
+ %span
+ - if diff.deleted_file
+ = diff.old_path
+ - else
+ = diff.new_path
+ - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
+ %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
.diff-content
%table
- note.truncated_diff_lines.each do |line|
@@ -19,8 +19,10 @@
%td.new_line= "..."
%td.line_content.matched= line.text
- else
- %td.old_line= raw(line.type == "new" ? " " : line.old_pos)
- %td.new_line= raw(line.type == "old" ? " " : line.new_pos)
+ %td.old_line{class: line.type == "new" ? "new" : "old"}
+ = raw(line.type == "new" ? " " : line.old_pos)
+ %td.new_line{class: line.type == "new" ? "new" : "old"}
+ = raw(line.type == "old" ? " " : line.new_pos)
%td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text)
- if line_code == note.line_code
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..43e92437cf577e64163efd308ca0526e86c3f1ac
--- /dev/null
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -0,0 +1,16 @@
+.panel.panel-default
+ .panel-heading
+ %strong #{@group.name}
+ group members
+ %small
+ (#{members.count})
+ .panel-head-actions
+ = link_to group_group_members_path(@group), class: 'btn btn-sm' do
+ %i.fa.fa-pencil-square-o
+ Edit group members
+ %ul.well-list
+ - members.each do |member|
+ = render 'groups/group_members/group_member', member: member, show_controls: false
+ - if members.count > 20
+ %li
+ and #{members.count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(@group)}
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..5daae2708e6a42caaba7cbb43e7a5f69db56ddf5
--- /dev/null
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -0,0 +1,15 @@
+= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f|
+ .form-group
+ = f.label :user_ids, "People", class: 'control-label'
+ .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all)
+
+ .form-group
+ = f.label :access_level, "Project Access", class: 'control-label'
+ .col-sm-10
+ = select_tag :access_level, options_for_select(ProjectMember.access_roles, @project_member.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
+
+ .form-actions
+ = f.submit 'Add users to project', class: "btn btn-create"
diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..a07d07623340fd62000138be7cea09bb96753405
--- /dev/null
+++ b/app/views/projects/project_members/_project_member.html.haml
@@ -0,0 +1,37 @@
+- user = member.user
+- return unless user
+
+%li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)}
+ %span.list-item-name
+ = image_tag avatar_icon(user.email, 16), class: "avatar s16"
+ %strong= user.name
+ %span.cgray= user.username
+ - if user == current_user
+ %span.label.label-success It's you
+ - if user.blocked?
+ %label.label.label-danger
+ %strong Blocked
+
+ - if current_user_can_admin_project
+ - unless @project.personal? && user == current_user
+ .pull-right
+ %strong= member.human_access
+ = button_tag class: "btn-xs btn js-toggle-button",
+ title: 'Edit access level', type: 'button' do
+ %i.fa.fa-pencil-square-o
+
+
+ - if current_user == user
+ = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: "Leave project?"}, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do
+ %i.fa.fa-minus.fa-inverse
+ - else
+ = link_to namespace_project_project_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from team' do
+ %i.fa.fa-minus.fa-inverse
+
+ .edit-member.hide.js-toggle-content
+ %br
+ = form_for member, as: :project_member, url: namespace_project_project_member_path(@project.namespace, @project, member.user), remote: true do |f|
+ .prepend-top-10
+ = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: 'form-control'
+ .prepend-top-10
+ = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..615c425e59ae2967a6ba90f5d7ff6ad8bd83103d
--- /dev/null
+++ b/app/views/projects/project_members/_team.html.haml
@@ -0,0 +1,11 @@
+- can_admin_project = can?(current_user, :admin_project, @project)
+
+.panel.panel-default.prepend-top-20
+ .panel-heading
+ %strong #{@project.name}
+ project members
+ %small
+ (#{members.count})
+ %ul.well-list
+ - members.each do |project_member|
+ = render 'project_member', member: project_member, current_user_can_admin_project: can_admin_project
diff --git a/app/views/projects/team_members/import.html.haml b/app/views/projects/project_members/import.html.haml
similarity index 66%
rename from app/views/projects/team_members/import.html.haml
rename to app/views/projects/project_members/import.html.haml
index d1f46c61b2e7d869fcce287a8fce9cf3ab3d6610..293754cd0c0fa180a5f80dec6fdb78738d88306a 100644
--- a/app/views/projects/team_members/import.html.haml
+++ b/app/views/projects/project_members/import.html.haml
@@ -3,12 +3,12 @@
%p.light
Only project members will be imported. Group members will be skipped.
%hr
-= form_tag apply_import_project_team_members_path(@project), method: 'post', class: 'form-horizontal' do
+= form_tag apply_import_namespace_project_project_members_path(@project.namespace, @project), method: 'post', class: 'form-horizontal' do
.form-group
= label_tag :source_project_id, "Project", class: 'control-label'
.col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true)
.form-actions
= button_tag 'Import project members', class: "btn btn-create"
- = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..36a6f6a15547d9d05737d5ff08f475a198531dae
--- /dev/null
+++ b/app/views/projects/project_members/index.html.haml
@@ -0,0 +1,35 @@
+%h3.page-title
+ Users with access to this project
+
+%p.light
+ Read more about project permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
+
+%hr
+
+.clearfix.js-toggle-container
+ = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
+ .form-group
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' }
+ = button_tag 'Search', class: 'btn'
+
+ - if can?(current_user, :admin_project_member, @project)
+ %span.pull-right
+ = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do
+ Add members
+ %i.fa.fa-chevron-down
+ = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do
+ Import members
+
+ .js-toggle-content.hide.new-group-member-holder
+ = render "new_project_member"
+
+= render "team", members: @project_members
+
+- if @group
+ = render "group_members", members: @group_members
+
+:coffeescript
+ $('form.member-search-form').on 'submit', (event) ->
+ event.preventDefault()
+ Turbolinks.visit @.action + '?' + $(@).serialize()
diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml
new file mode 100644
index 0000000000000000000000000000000000000000..811b1858821c93518116a449322dc0b161954f77
--- /dev/null
+++ b/app/views/projects/project_members/update.js.haml
@@ -0,0 +1,3 @@
+- can_admin_project = can?(current_user, :admin_project, @project)
+:plain
+ $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member, current_user_can_admin_project: can_admin_project))}');
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..bb49f4de8736ce2422f605b09c67824ac4ef0040
--- /dev/null
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -0,0 +1,34 @@
+- unless @branches.empty?
+ %br
+ %h4 Already Protected:
+ %table.table.protected-branches-list
+ %thead
+ %tr.no-border
+ %th Branch
+ %th Developers can push
+ %th Last commit
+ %th
+
+ %tbody
+ - @branches.each do |branch|
+ - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch)
+ %tr
+ %td
+ = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do
+ %strong= branch.name
+ - if @project.root_ref?(branch.name)
+ %span.label.label-info default
+ %td
+ = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url
+ %td
+ - if commit = branch.commit
+ = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do
+ = commit.short_id
+ ·
+ #{time_ago_with_tooltip(commit.committed_date)}
+ - else
+ (branch was removed from repository)
+ %td
+ .pull-right
+ - if can? current_user, :admin_project, @project
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 227a2f9a0619e4a7983f4d442ad7da80571e65d7..a3464c0e5e183d623abb99c5372b588349c9aee3 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -2,7 +2,7 @@
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
-.bs-callout.bs-callout-info
+.well.append-bottom-20
%p Protected branches are designed to
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
@@ -11,7 +11,7 @@
%p Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"}
- if can? current_user, :admin_project, @project
- = form_for [@project, @protected_branch], html: { class: 'form-horizontal' } do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'form-horizontal' } do |f|
-if @protected_branch.errors.any?
.alert.alert-danger
%ul
@@ -22,29 +22,14 @@
= f.label :name, "Branch", class: 'control-label'
.col-sm-10
= f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "select2"})
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :developers_can_push do
+ = f.check_box :developers_can_push
+ %strong Developers can push
+ .help-block Allow developers to push to this branch
.form-actions
= f.submit 'Protect', class: "btn-create btn"
-- unless @branches.empty?
- %h5 Already Protected:
- %ul.bordered-list.protected-branches-list
- - @branches.each do |branch|
- %li
- %h4
- = link_to project_commits_path(@project, branch.name) do
- %strong= branch.name
- - if @project.root_ref?(branch.name)
- %span.label.label-info default
- %span.label.label-success
- %i.fa.fa-lock
- .pull-right
- - if can? current_user, :admin_project, @project
- = link_to 'Unprotect', [@project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-small"
+= render 'branches_list'
- - if commit = branch.commit
- = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do
- = commit.short_id
- %span.light
- = gfm escape_once(truncate(commit.title, length: 40))
- #{time_ago_with_tooltip(commit.committed_date)}
- - else
- (branch was removed from repository)
diff --git a/app/views/projects/refs/logs_tree.js.haml b/app/views/projects/refs/logs_tree.js.haml
index 948a21aa8165594307907fbeb795f3685fe0b5c8..49ce6c0888ea0d8cb996b2ccc3952f1394e45956 100644
--- a/app/views/projects/refs/logs_tree.js.haml
+++ b/app/views/projects/refs/logs_tree.js.haml
@@ -11,9 +11,9 @@
- if @logs.present?
:plain
var current_url = location.href.replace(/\/?$/, '/');
- var log_url = '#{project_tree_url(@project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/');
+ var log_url = '#{namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/');
if(current_url == log_url) {
// Load 10 more commit log for each file in tree
// if we still on the same page
- ajaxGet('#{logs_file_project_ref_path(@project, @ref, @path || '/', offset: (@offset + @limit))}');
+ ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '/', offset: (@offset + @limit))}');
}
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index ce69adeb48cd8751038fe3c7cf9ce17ddf4ed6c6..1ba7a1f206020ed9f4910fbf024287a203757947 100644
--- a/app/views/projects/repositories/_download_archive.html.haml
+++ b/app/views/projects/repositories/_download_archive.html.haml
@@ -3,35 +3,35 @@
- split_button = split_button || false
- if split_button == true
%span.btn-group{class: btn_class}
- = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-sm', rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
- %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+ %a.btn-sm.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Archive Format
%ul.dropdown-menu{ role: 'menu' }
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar.gz
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar.bz2
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar
- else
%span.btn-group{class: btn_class}
- = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
%i.fa.fa-download
%span zip
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do
%i.fa.fa-download
%span tar.gz
diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml
index c77ffff43fe02df137a9b432996b9538cfa4dc86..f3526ad0747a8dbe31117f5e0dd7308efc9c3db1 100644
--- a/app/views/projects/repositories/_feed.html.haml
+++ b/app/views/projects/repositories/_feed.html.haml
@@ -1,7 +1,7 @@
- commit = update
%tr
%td
- = link_to project_commits_path(@project, commit.head.name) do
+ = link_to namespace_project_commits_path(@project.namespace, @project, commit.head.name) do
%strong
= commit.head.name
- if @project.root_ref?(commit.head.name)
@@ -9,7 +9,7 @@
%td
%div
- = link_to project_commits_path(@project, commit.id) do
+ = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do
%code= commit.short_id
= image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: ''
= gfm escape_once(truncate(commit.title, length: 40))
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 1151f22c7e8e78076fba96fd25754c9412b0c01d..ce6b7a0737a8c0fa344687710d8f2d2f0cae0666 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -5,12 +5,12 @@
%p= @service.description
.back-link
- = link_to project_services_path(@project) do
+ = link_to namespace_project_services_path(@project.namespace, @project) do
← to services
%hr
-= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
+= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
- if @service.errors.any?
.alert.alert-danger
%ul
@@ -18,24 +18,72 @@
%li= msg
- if @service.help.present?
- .bs-callout
- = @service.help
+ .well
+ = preserve do
+ = markdown @service.help
.form-group
= f.label :active, "Active", class: "control-label"
.col-sm-10
= f.check_box :active
+ - if @service.supported_events.length > 1
+ .form-group
+ = f.label :url, "Trigger", class: 'control-label'
+ .col-sm-10
+ - if @service.supported_events.include?("push")
+ %div
+ = f.check_box :push_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :push_events, class: 'list-label' do
+ %strong Push events
+ %p.light
+ This url will be triggered by a push to the repository
+ - if @service.supported_events.include?("tag_push")
+ %div
+ = f.check_box :tag_push_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :tag_push_events, class: 'list-label' do
+ %strong Tag push events
+ %p.light
+ This url will be triggered when a new tag is pushed to the repository
+ - if @service.supported_events.include?("note")
+ %div
+ = f.check_box :note_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :note_events, class: 'list-label' do
+ %strong Comments
+ %p.light
+ This url will be triggered when someone adds a comment
+ - if @service.supported_events.include?("issue")
+ %div
+ = f.check_box :issues_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :issues_events, class: 'list-label' do
+ %strong Issues events
+ %p.light
+ This url will be triggered when an issue is created
+ - if @service.supported_events.include?("merge_request")
+ %div
+ = f.check_box :merge_requests_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :merge_requests_events, class: 'list-label' do
+ %strong Merge Request events
+ %p.light
+ This url will be triggered when a merge request is created
+
- @service.fields.each do |field|
- name = field[:name]
- - value = @service.send(name) unless field[:type] == 'password'
+ - title = field[:title] || name.humanize
+ - value = service_field_value(field[:type], @service.send(name))
- type = field[:type]
- placeholder = field[:placeholder]
- choices = field[:choices]
- default_choice = field[:default_choice]
+ - help = field[:help]
.form-group
- = f.label name, class: "control-label"
+ = f.label name, title, class: "control-label"
.col-sm-10
- if type == 'text'
= f.text_field name, class: "form-control", placeholder: placeholder
@@ -46,10 +94,13 @@
- elsif type == 'select'
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
- elsif type == 'password'
- = f.password_field name, class: 'form-control'
+ = f.password_field name, placeholder: value, class: 'form-control'
+ - if help
+ %span.help-block= help
.form-actions
= f.submit 'Save', class: 'btn btn-save'
- - if @service.valid? && @service.activated? && @service.can_test?
- = link_to 'Test settings', test_project_service_path(@project, @service.to_param), class: 'btn'
+ - if @service.valid? && @service.activated?
+ - disabled = @service.can_test? ? '':'disabled'
+ = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}"
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 7271dd830ca76f2e9b73cce73dbd10ff5170c31c..0d3ccb6bb839e7306e5b9409fee7e309d11eea3e 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,13 +1,22 @@
%h3.page-title Project services
%p.light Project services allow you to integrate GitLab with other applications
-%hr
-%ul.bordered-list
+%table.table
+ %thead
+ %tr
+ %th
+ %th Service
+ %th Description
+ %th Last edit
- @services.sort_by(&:title).each do |service|
- %li
- %h4
- = link_to edit_project_service_path(@project, service.to_param) do
- = service.title
- .pull-right
- = boolean_to_icon service.activated?
- %p= service.description
+ %tr
+ %td
+ = boolean_to_icon service.activated?
+ %td
+ = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
+ %strong= service.title
+ %td
+ = service.description
+ %td.light
+ = time_ago_in_words service.updated_at
+ ago
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 9b06ebe95a4d2b2840c440e28721ae2a932171de..85113ffa7e23385c33103c12560d1b7d8b6432da 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,3 +1,7 @@
+- if current_user && can?(current_user, :download_code, @project)
+ = render 'shared/no_ssh'
+ = render 'shared/no_password'
+
= render "home_panel"
- readme = @repository.readme
@@ -11,20 +15,22 @@
Readme
.project-home-links
- unless @project.empty_repo?
- = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
- = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), project_branches_path(@project)
- = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), project_tags_path(@project)
+ = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref)
+ = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), namespace_project_branches_path(@project.namespace, @project)
+ = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), namespace_project_tags_path(@project.namespace, @project)
%span.light.prepend-left-20= repository_size
.tab-content
.tab-pane.active#tab-activity
.row
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
%section.col-md-9
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.content_list
= spinner
- %aside.col-md-3.project-side.hidden-sm.hidden-xs
+ %aside.col-md-3.project-side
.clearfix
- if @project.archived?
.alert.alert-warning
@@ -34,50 +40,68 @@
%p Repository is read-only
- if @project.forked_from_project
- .alert.alert-success
+ .well
%i.fa.fa-code-fork.project-fork-icon
Forked from:
%br
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
- unless @project.empty_repo?
- = link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
+ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
Compare code
- - if @repository.version
- - version = @repository.version
- = link_to project_blob_path(@project, tree_join(@repository.root_ref, version.name)), class: 'btn btn-block' do
+ - if version = @repository.version
+ - detail_url = changelog_url(@project) || version_url(@project)
+ = link_to detail_url, class: 'btn btn-block' do
Version:
%span.count
= @repository.blob_by_oid(version.id).data
+ - elsif @repository.changelog
+ = link_to changelog_url(@project), class: 'btn btn-block' do
+ View changelog
- .prepend-top-10
+ - if @repository.contribution_guide
+ = link_to contribution_guide_url(@project), class: 'btn btn-block' do
+ View contribution guide
+
+ - if @repository.license
+ = link_to license_url(@project), class: 'btn btn-block' do
+ View license
+
+ .prepend-top-10.append-bottom-10
%p
%span.light Created on
#{@project.created_at.stamp('Aug 22, 2013')}
%p
- %span.light Owned by
+ %span.light Owned by #{@project.group ? "the" : nil}
- if @project.group
#{link_to @project.group.name, @project.group} group
- else
#{link_to @project.owner_name, @project.owner}
+ - unless @project.empty_repo?
+ - if can? current_user, :download_code, @project
+ %hr
+ .prepend-top-10.append-bottom-10
+ = render 'projects/repositories/download_archive', split_button: true
+
+ .prepend-top-10
- @project.ci_services.each do |ci_service|
- if ci_service.active? && ci_service.respond_to?(:builds_path)
+ %hr
- if ci_service.respond_to?(:status_img_path)
- = link_to ci_service.builds_path do
+ = link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
= image_tag ci_service.status_img_path, alt: "build status"
- else
%span.light CI provided by
- = link_to ci_service.title, ci_service.builds_path
+ = link_to ci_service.title, ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink'
- if readme
.tab-pane#tab-readme
%article.readme-holder#README
- = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
%h4.readme-file-title
%i.fa.fa-file
= readme.name
.wiki
= render_readme(readme)
-
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index f6a5bf9e4ff74d508e454d18d81c41565a7eb255..2d4d5d030ab3ec37c7601b4dc9d114bfec25621f 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,4 +1,4 @@
%h3.page-title
Edit snippet
%hr
-= render "shared/snippets/form", url: project_snippet_path(@project, @snippet)
+= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet)
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index e60f9a44322d2ade2e2f0f8d4ef0e1d60fd6c168..e2d8ec673a17fe3f2e6ced456fbf3d9b5da3ba22 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
Snippets
- if can? current_user, :write_project_snippet, @project
- = link_to new_project_snippet_path(@project), class: "btn btn-new pull-right", title: "New Snippet" do
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do
Add new snippet
%p.light
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 10f684b63162650109bf1d747a4d5cc8e66ace7e..bb659dba0cf1cb21504f6796f835193055565305 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,4 +1,4 @@
%h3.page-title
New snippet
%hr
-= render "shared/snippets/form", url: project_snippets_path(@project, @snippet)
+= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet)
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index ada0d30c49643e37012d20d4fc5bd5c13c47d57d..d19689a1056213a16988e15c975c09e4c2e4d888 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -2,7 +2,7 @@
= @snippet.title
.pull-right
- = link_to new_project_snippet_path(@project), class: "btn btn-new", title: "New Snippet" do
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
Add new snippet
%hr
@@ -17,21 +17,21 @@
= @snippet.author_name
.back-link
- = link_to project_snippets_path(@project) do
+ = link_to namespace_project_snippets_path(@project.namespace, @project) do
← project snippets
.file-holder
.file-title
%i.fa.fa-file
- %span.file_name
+ %strong
= @snippet.file_name
- .options
+ .file-actions
.btn-group
- if can?(current_user, :modify_project_snippet, @snippet)
- = link_to "edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small", title: 'Edit Snippet'
- = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-small", target: "_blank"
+ = link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", title: 'Edit Snippet'
+ = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
- if can?(current_user, :admin_project_snippet, @snippet)
- = link_to "remove", project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-small btn-remove", title: 'Delete Snippet'
+ = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
= render 'shared/snippets/blob'
%div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index f93c1b4211fd5c9693e2ff63c127b846dd9ec1e7..f22308e54b04e48c56b48904ae80cb9ef27d0eec 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -1,17 +1,17 @@
- commit = @repository.commit(tag.target)
%li
%h4
- = link_to project_commits_path(@project, tag.name), class: "" do
+ = link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do
%i.fa.fa-tag
= tag.name
- if tag.message.present?
- = tag.message
+ = strip_gpg_signature(tag.message)
.pull-right
- if can? current_user, :download_code, @project
- = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-small'
+ = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-sm'
- if can?(current_user, :admin_project, @project)
- = link_to project_tag_path(@project, tag.name), class: 'btn btn-small btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
+ = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-sm btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
%i.fa.fa-trash-o
- if commit
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index ac74e3b6d3642c699f52aef8acc138bff0209c28..f1bc2bc9a2b23aa8cfc5526a610b7b4c59bb0c6f 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -4,7 +4,7 @@
Git Tags
- if can? current_user, :push_code, @project
.pull-right
- = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do
+ = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
%i.fa.fa-add-sign
New tag
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index ad7ff8d3db8c2b7f50bdd3b11c8f5b300a402c6c..655044438d50a412085d3e9e2ee351fefebb8ccb 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -5,7 +5,7 @@
%h3.page-title
%i.fa.fa-code-fork
New tag
-= form_tag project_tags_path, method: :post, class: "form-horizontal" do
+= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do
.form-group
= label_tag :tag_name, 'Name for new tag', class: 'control-label'
.col-sm-10
@@ -22,9 +22,10 @@
.light (Optional) Entering a message will create an annotated tag.
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
- = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
+ = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel'
:javascript
+ disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create");
var availableTags = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml
deleted file mode 100644
index 2bf61fa12bb1c26c17b62f7c5f6aea33573aeeb9..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/_form.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-%h3.page-title
- New project member(s)
-
-= form_for @user_project_relation, as: :project_member, url: project_team_members_path(@project), html: { class: "form-horizontal users-project-form" } do |f|
- -if @user_project_relation.errors.any?
- .alert.alert-danger
- %ul
- - @user_project_relation.errors.full_messages.each do |msg|
- %li= msg
-
- %p 1. Choose people you want in the project
- .form-group
- = f.label :user_ids, "People", class: 'control-label'
- .col-sm-10
- = users_select_tag(:user_ids, multiple: true)
-
- %p 2. Set access level for them
- .form-group
- = f.label :access_level, "Project Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
-
- .form-actions
- = f.submit 'Add users', class: "btn btn-create"
- = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
diff --git a/app/views/projects/team_members/_group_members.html.haml b/app/views/projects/team_members/_group_members.html.haml
deleted file mode 100644
index df3c914fdea7c2712d31a0d8f92c4bee583028f7..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/_group_members.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-- group_users_count = @group.group_members.count
-.panel.panel-default
- .panel-heading
- %strong #{@group.name}
- group members (#{group_users_count})
- .pull-right
- = link_to members_group_path(@group), class: 'btn btn-small' do
- %i.fa.fa-pencil-square-o
- %ul.well-list
- - @group.group_members.order('access_level DESC').limit(20).each do |member|
- = render 'groups/group_members/group_member', member: member, show_controls: false
- - if group_users_count > 20
- %li
- and #{group_users_count - 20} more. For full list visit #{link_to 'group members page', members_group_path(@group)}
diff --git a/app/views/projects/team_members/_team.html.haml b/app/views/projects/team_members/_team.html.haml
deleted file mode 100644
index 0e5b8176132a91ad02c981f9e4e0c9cf8024b9fa..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/_team.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-.team-table
- - can_admin_project = (can? current_user, :admin_project, @project)
- .panel.panel-default
- .panel-heading
- %strong #{@project.name}
- project members (#{members.count})
- %ul.well-list
- - members.each do |team_member|
- = render 'team_member', member: team_member, current_user_can_admin_project: can_admin_project
diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml
deleted file mode 100644
index 7a9c0939ba04ebb560610bcec97e85c5860205f0..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/_team_member.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- user = member.user
-%li{id: dom_id(user), class: "team_member_row access-#{member.human_access.downcase}"}
- .pull-right
- - if current_user_can_admin_project
- - unless @project.personal? && user == current_user
- .pull-left
- = form_for(member, as: :project_member, url: project_team_member_path(@project, member.user)) do |f|
- = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "trigger-submit"
-
- = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do
- %i.fa.fa-minus.fa-inverse
- = image_tag avatar_icon(user.email, 32), class: "avatar s32"
- %p
- %strong= user.name
- %span.cgray= user.username
-
-
diff --git a/app/views/projects/team_members/index.html.haml b/app/views/projects/team_members/index.html.haml
deleted file mode 100644
index ecb7c689e8ab9d882339ade2ee076ce369799597..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/index.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-%h3.page-title
- Users with access to this project
-
- - if can? current_user, :admin_team_member, @project
- %span.pull-right
- = link_to new_project_team_member_path(@project), class: "btn btn-new btn-grouped", title: "New project member" do
- New project member
- = link_to import_project_team_members_path(@project), class: "btn btn-grouped", title: "Import members from another project" do
- Import members
-
-%p.light
- Read more about project permissions
- %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
-= render "team", members: @project_members
-- if @group
- = render "group_members"
diff --git a/app/views/projects/team_members/new.html.haml b/app/views/projects/team_members/new.html.haml
deleted file mode 100644
index b1bc3ba0eba684a146b503fa89225bda4614e840..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/new.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-= render "form"
diff --git a/app/views/projects/team_members/update.js.haml b/app/views/projects/team_members/update.js.haml
deleted file mode 100644
index c68fe9574a2526a4fb136c22bd2d1428919aad4b..0000000000000000000000000000000000000000
--- a/app/views/projects/team_members/update.js.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-- if @user_project_relation.valid?
- :plain
- $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#529214"}, 1000);;
-- else
- :plain
- $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#D12F19"}, 1000);;
diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml
index 10b0de98c041494e71a612aa79396214566b29f9..17b9fecfeb162f8f3ac88523a7fbe336c4d46516 100644
--- a/app/views/projects/transfer.js.haml
+++ b/app/views/projects/transfer.js.haml
@@ -1,7 +1,2 @@
-- if @project.errors[:namespace_id].present?
- :plain
- $("#tab-transfer .errors-holder").replaceWith(errorMessage('#{escape_javascript(@project.errors[:namespace_id].first)}'));
- $("#tab-transfer .form-actions input").removeAttr('disabled').removeClass('disabled');
-- else
- :plain
- location.href = "#{edit_project_path(@project)}";
+:plain
+ location.href = "#{edit_namespace_project_path(@project.namespace, @project)}";
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index 393ef0e24bd725416472b16d22fc4d620f3e2117..02ecbade219250ceaf038830c7f9da2361a5baba 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -1,8 +1,8 @@
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
- = tree_icon(type)
+ = tree_icon(type, blob_item.mode, blob_item.name)
%span.str-truncated
- = link_to blob_item.name, project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name))
+ = link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
%td.tree_time_ago.cgray
= render 'spinner'
%td.hidden-xs.tree_commit
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
index 46e9be4af83bd73a08616d61cfe5b9d7503450d7..2b5f671c09ec5e4a1e268ecaa20f1578119c650d 100644
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ b/app/views/projects/tree/_submodule_item.html.haml
@@ -1,14 +1,6 @@
-- tree, commit = submodule_links(submodule_item)
%tr{ class: "tree-item" }
%td.tree-item-file-name
- %i.fa.fa-archive
- %span
- = link_to truncate(submodule_item.name, length: 40), tree
- @
- %span.monospace
- - if commit.nil?
- #{truncate_sha(submodule_item.id)}
- - else
- = link_to "#{truncate_sha(submodule_item.id)}", commit
+ %i.fa.fa-archive.fa-fw
+ = submodule_link(submodule_item, @ref)
%td
%td.hidden-xs
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 1159fcadffd9aa775c32c21ca2640816dbd9ba7d..9f43a1288aa5bcd429569a60c5267a1124b0930c 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -1,18 +1,20 @@
%ul.breadcrumb.repo-breadcrumb
%li
- = link_to project_tree_path(@project, @ref) do
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
= @project.path
- tree_breadcrumbs(tree, 6) do |title, path|
%li
- if path
- = link_to truncate(title, length: 40), project_tree_path(@project, path)
+ = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else
= link_to title, '#'
- if current_user && can_push_branch?(@project, @ref)
%li
- = link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
- %small
- %i.fa.fa-plus
+ = link_to project_new_tree_path(@project, @id), class: 'btn btn-small btn-xs', title: 'Create New file', id: 'new-file-link' do
+ Create
+ = |
+ = button_tag class: 'btn btn-small btn-xs', 'data-toggle' => 'modal', title: 'Upload New file', 'data-target' => '#modal-upload-tree' do
+ Upload
%div#tree-content-holder.tree-content-holder
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
@@ -27,15 +29,15 @@
%i.fa.fa-angle-right
%small.light
- = link_to @commit.short_id, project_commit_path(@project, @commit)
+ = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
–
= truncate(@commit.title, length: 50)
- = link_to 'History', project_commits_path(@project, @id), class: 'pull-right'
+ = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
- if @path.present?
%tr.tree-item
%td.tree-item-file-name
- = link_to "..", project_tree_path(@project, up_dir_path(tree)), class: 'prepend-left-10'
+ = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10'
%td
%td.hidden-xs
@@ -44,8 +46,10 @@
- if tree.readme
= render "projects/tree/readme", readme: tree.readme
+
%div.tree_progress
+
:javascript
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml
index bd50dd4d9a2d8e6681b3799dd5f353718d407da2..50521264a611c386a9813fb6767d9dca1b4e7f54 100644
--- a/app/views/projects/tree/_tree_commit_column.html.haml
+++ b/app/views/projects/tree/_tree_commit_column.html.haml
@@ -1,3 +1,3 @@
%span.str-truncated
%span.tree_author= commit_author_link(commit, avatar: true, size: 16)
- = link_to_gfm commit.title, project_commit_path(@project, commit.id), class: "tree-commit-link"
+ = link_to_gfm commit.title, namespace_project_commit_path(@project.namespace, @project, commit.id), class: "tree-commit-link"
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
index f8cecf9be1f14daba52a4c2b708e8410031e4213..e87138bf9800bd7d4349d4b450a6dfb3af741353 100644
--- a/app/views/projects/tree/_tree_item.html.haml
+++ b/app/views/projects/tree/_tree_item.html.haml
@@ -1,8 +1,9 @@
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%td.tree-item-file-name
- = tree_icon(type)
+ = tree_icon(type, tree_item.mode, tree_item.name)
%span.str-truncated
- = link_to tree_item.name, project_tree_path(@project, tree_join(@id || @commit.id, tree_item.name))
+ - path = flatten_tree(tree_item)
+ = link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
%td.tree_time_ago.cgray
= render 'spinner'
%td.hidden-xs.tree_commit
diff --git a/app/views/projects/tree/_upload.html.haml b/app/views/projects/tree/_upload.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..99ee6595010285a803093b4a6fd23a6920f98f53
--- /dev/null
+++ b/app/views/projects/tree/_upload.html.haml
@@ -0,0 +1,35 @@
+#modal-upload-tree.modal.hide
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3.page-title Upload
+ %p.light
+ From branch
+ %strong= @ref
+ .modal-body
+ = form_tag project_new_tree_path(@project, @id), method: :put, class: 'form-horizontal', :multipart => true , :onChange => 'checkFile()' do
+ %br
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ = file_field_tag :file_upload, :id => "file", :class => "file_up"
+ %br
+ = render 'shared/commit_message_container', params: params,
+ placeholder: 'Upload this file because...'
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ = button_tag 'Upload file', class: 'btn btn-small btn-primary'
+ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+:javascript
+ function checkFile(){
+ str=document.getElementById('file').value;
+ if(str.length < 1){
+ alert('Please select file!');
+ }
+ }
+ disableButtonIfAnyEmptyField( '#commit_message')
+
+
+
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index fc4616da6ec626bc35d107242ec7ec6824fd8bed..1cff71018e4a1c9c06c9acf9e5331cb26af48e3d 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -3,7 +3,16 @@
- if can? current_user, :download_code, @project
.tree-download-holder
- = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group-small pull-right hidden-xs hidden-sm', split_button: true
+ = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group-sm pull-right hidden-xs hidden-sm', split_button: true
+
+- if allowed_tree_edit?
+ = render 'projects/tree/upload'
+
+- if allowed_tree_edit?
+ = render 'projects/tree/upload'
#tree-holder.tree-holder.clearfix
= render "tree", tree: @tree
+
+
+
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index cbb21f2b9fb03229e6a95b623bfd0e2f8803e078..4f3f4cab8d5c7b8df6afbfc79d7715fcd963ca4c 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -1,6 +1,6 @@
- if @project.valid?
:plain
- location.href = "#{edit_project_path(@project)}";
+ location.href = "#{edit_namespace_project_path(@project.namespace, @project)}";
- else
:plain
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index f37c086716d04b88e26354e579d86016427fdb81..9fbfa0b1aebcc1adba1eb1dfc6c06d06534ca945 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f|
-if @page.errors.any?
#error_explanation
.alert.alert-danger
@@ -19,13 +19,15 @@
%code [Link Title](page-slug)
\.
- .form-group
+ .form-group.wiki-content
= f.label :content, class: 'control-label'
.col-sm-10
- = render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
- .col-sm-12.hint
- .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
- .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+ = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
+ = render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
+ .col-sm-12.hint
+ .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
+ .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+
.clearfix
.error-alert
.form-group
@@ -35,11 +37,10 @@
.form-actions
- if @page && @page.persisted?
= f.submit 'Save changes', class: "btn-save btn"
- = link_to "Cancel", project_wiki_path(@project, @page), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-cancel"
- else
= f.submit 'Create page', class: "btn-create btn"
- = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel"
:javascript
- window.project_image_path_upload = "#{upload_image_project_path @project}";
-
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 30410bc95e0b7bf87402acad2782ec8672cd3b0f..633214a4e869f9d091a47b2968298b5bfa046c4c 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,8 +1,8 @@
%span.pull-right
- if (@page && @page.persisted?)
- = link_to history_project_wiki_path(@project, @page), class: "btn btn-grouped" do
+ = link_to history_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
Page History
- if can?(current_user, :write_wiki, @project)
- = link_to edit_project_wiki_path(@project, @page), class: "btn btn-grouped" do
+ = link_to edit_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 90539fde583a2394754227159e930253e84733a7..693c3facb3286c1f8e9805d2e37fcfe578ee6448 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,12 +1,12 @@
%ul.nav.nav-tabs
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
- = link_to 'Home', project_wiki_path(@project, :home)
+ = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
= nav_link(path: 'wikis#pages') do
- = link_to 'Pages', pages_project_wikis_path(@project)
+ = link_to 'Pages', pages_namespace_project_wikis_path(@project.namespace, @project)
= nav_link(path: 'wikis#git_access') do
- = link_to git_access_project_wikis_path(@project) do
+ = link_to git_access_namespace_project_wikis_path(@project.namespace, @project) do
%i.fa.fa-download
Git Access
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index 1ce292a02dfbd0f611cd1c3bf8bc491b05998cc7..6834969de8b44e3a77b57b5f05fc6758d44e23d3 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -7,7 +7,7 @@
.modal-body
= label_tag :new_wiki_path do
%span Page slug
- = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project)
+ = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project)
%p.hint
Please don't use spaces.
.modal-footer
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 5347caf000a4e3e1248edede24a1c6e2211ada6a..566850cb78d3487b10593cc71ae2aa8e41c3d203 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -9,5 +9,5 @@
.pull-right
- if @page.persisted? && can?(current_user, :admin_wiki, @project)
- = link_to project_wiki_path(@project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-small btn-remove" do
+ = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-sm btn-remove" do
Delete this page
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index ef4b8f74714eaadd00457523b80acfa43982fd33..91291f753f772e66b579fa0c97cecfcfb8a69aab 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,7 +1,7 @@
= render 'nav'
%h3.page-title
%span.light History for
- = link_to @page.title, project_wiki_path(@project, @page)
+ = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
%table.table
%thead
@@ -12,18 +12,19 @@
%th Last updated
%th Format
%tbody
- - @page.versions.each do |version|
+ - @page.versions.each_with_index do |version, index|
- commit = version
%tr
%td
- = link_to project_wiki_path(@project, @page, version_id: commit.id) do
+ = link_to project_wiki_path_with_version(@project, @page,
+ commit.id, index == 0) do
= truncate_sha(commit.id)
%td
= commit.author.name
%td
= commit.message
%td
- #{time_ago_with_tooltip(version.date)}
+ #{time_ago_with_tooltip(version.authored_date)}
%td
%strong
= @page.page.wiki.page(@page.page.name, commit.id).try(:format)
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 264b48ec36c057fdb2d4667e9e9173583917c6a5..ee233d9086f99d02406169cf9881d7dec7cd40ba 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -5,7 +5,7 @@
- @wiki_pages.each do |wiki_page|
%li
%h4
- = link_to wiki_page.title, project_wiki_path(@project, wiki_page)
+ = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index ede4fef9e24a696ef017c961d228792d0080759c..a6263e93f678e515a11429df7755a25772b0af66 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -5,7 +5,7 @@
- if @page.historical?
.warning_message
This is an old version of this page.
- You can view the #{link_to "most recent version", project_wiki_path(@project, @page)} or browse the #{link_to "history", history_project_wiki_path(@project, @page)}.
+ You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", history_namespace_project_wiki_path(@project.namespace, @project, @page)}.
%hr
diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml
index eca69ce50b1f55840c83e487fe1b03375a404769..ffc145497abe08dbd90335d997b3cc7272d7a0d8 100644
--- a/app/views/search/_filter.html.haml
+++ b/app/views/search/_filter.html.haml
@@ -1,5 +1,5 @@
.dropdown.inline
- %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
%i.fa.fa-tags
%span.light Group:
- if @group.present?
@@ -17,7 +17,7 @@
= group.name
.dropdown.inline.prepend-left-10.project-filter
- %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
%i.fa.fa-tags
%span.light Project:
- if @project.present?
diff --git a/app/views/search/_project_filter.html.haml b/app/views/search/_project_filter.html.haml
index c201b3d6c472f7b831d0a1987b345975c9e673cb..ad933502a282d928dd92c93c9f604b1f480936bd 100644
--- a/app/views/search/_project_filter.html.haml
+++ b/app/views/search/_project_filter.html.haml
@@ -25,6 +25,7 @@
= @search_results.notes_count
%li{class: ("active" if @scope == 'wiki_blobs')}
= link_to search_filter_path(scope: 'wiki_blobs') do
+ %i.fa.fa-book
Wiki
.pull-right
= @search_results.wiki_blobs_count
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 58bcff9dbe33258891795ea9b42d85ca3feacf44..796dd752a4c8db68a17e067080dd7a97c3c2a79a 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -2,7 +2,7 @@
#{@search_results.total_count} results found
- unless @show_snippets
- if @project
- for #{link_to @project.name_with_namespace, @project}
+ for #{link_to @project.name_with_namespace, [@project.namespace.becomes(Namespace), @project]}
- elsif @group
for #{link_to @group.name, @group}
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index b46b4832e19a51567fe0badbbe0e80f7cff504d6..84e9be82c444e2596cc2c95472ed282a9e5dd18f 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,9 +1,9 @@
.blob-result
.file-holder
.file-title
- = link_to project_blob_path(@project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
%i.fa.fa-file
%strong
= blob.filename
.file-content.code.term
- = render 'shared/file_hljs', blob: blob, first_line_number: blob.startline
+ = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, user_color_scheme_class: 'white'
diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml
index 7868f958261cabe3072a5cbb0eda1695af104542..ce8ddff955690ea4895580cdb23945e75fd8bd7d 100644
--- a/app/views/search/results/_issue.html.haml
+++ b/app/views/search/results/_issue.html.haml
@@ -1,6 +1,6 @@
.search-result-row
%h4
- = link_to [issue.project, issue] do
+ = link_to [issue.project.namespace.becomes(Namespace), issue.project, issue] do
%span.term.str-truncated= issue.title
.pull-right ##{issue.iid}
- if issue.description.present?
diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml
index 56b185283bd0bbda7866aae688e2af9e698669a2..2efa616d6644e453a53415d6fcf8fbdac5eb4c33 100644
--- a/app/views/search/results/_merge_request.html.haml
+++ b/app/views/search/results/_merge_request.html.haml
@@ -1,6 +1,6 @@
.search-result-row
%h4
- = link_to [merge_request.target_project, merge_request] do
+ = link_to [merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request] do
%span.term.str-truncated= merge_request.title
.pull-right ##{merge_request.iid}
- if merge_request.description.present?
diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml
index a44a4542df50842d0d19a4e7caa0317107ec5da7..5fcba2b7e93b22938d73635dad5295ceb476329b 100644
--- a/app/views/search/results/_note.html.haml
+++ b/app/views/search/results/_note.html.haml
@@ -9,7 +9,7 @@
= link_to project do
= project.name_with_namespace
·
- = link_to project_commit_path(project, note.commit_id, anchor: dom_id(note)) do
+ = link_to namespace_project_commit_path(project.namespace, project, note.commit_id, anchor: dom_id(note)) do
Commit #{truncate_sha(note.commit_id)}
- else
= link_to project do
@@ -17,7 +17,7 @@
·
%span #{note.noteable_type.titleize} ##{note.noteable.iid}
·
- = link_to [project, note.noteable, anchor: dom_id(note)] do
+ = link_to [project.namespace.becomes(Namespace), project, note.noteable, anchor: dom_id(note)] do
= note.noteable.title
.note-search-result
diff --git a/app/views/search/results/_project.html.haml b/app/views/search/results/_project.html.haml
index 301b65eca2964e78294a1943e64f2a2f77e02275..195cf06c8ea73fa1b208d7f7556ddef491985ae8 100644
--- a/app/views/search/results/_project.html.haml
+++ b/app/views/search/results/_project.html.haml
@@ -1,6 +1,6 @@
.search-result-row
%h4
- = link_to project do
+ = link_to [project.namespace.becomes(Namespace), project] do
%span.term= project.name_with_namespace
- if project.description.present?
%span.light.term= project.description
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index 6fc2cdf6362c4a991cb2f28b05e4315d017c1766..8af393777f0c32d4dde8597c762967769d1ae366 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -13,12 +13,6 @@
.file-title
%i.fa.fa-file
%strong= snippet_blob[:snippet_object].file_name
- %span.options
- .btn-group.tree-btn-group.pull-right
- - if snippet_blob[:snippet_object].author == current_user
- = link_to "Edit", edit_snippet_path(snippet_blob[:snippet_object]), class: "btn btn-tiny", title: 'Edit Snippet'
- = link_to "Delete", snippet_path(snippet_blob[:snippet_object]), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-tiny", title: 'Delete Snippet'
- = link_to "Raw", raw_snippet_path(snippet_blob[:snippet_object]), class: "btn btn-tiny", target: "_blank"
- if gitlab_markdown?(snippet_blob[:snippet_object].file_name)
.file-content.wiki
- snippet_blob[:snippet_chunks].each do |snippet|
diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml
index f7e5ee5e20e67f0e13d62509dd2cdf510791f7ac..c414acb6a11118080b000133a372f6df57984bc9 100644
--- a/app/views/search/results/_snippet_title.html.haml
+++ b/app/views/search/results/_snippet_title.html.haml
@@ -11,7 +11,7 @@
%small.pull-right.cgray
- if snippet_title.project_id?
- = link_to snippet_title.project.name_with_namespace, project_path(snippet_title.project)
+ = link_to snippet_title.project.name_with_namespace, namespace_project_path(snippet_title.project.namespace, snippet_title.project)
.snippet-info
= "##{snippet_title.id}"
diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml
index e361074b6a0bf14439c24e7548bc5a3f2a77d0ae..f9c5810e3d057962ec92eb8f913e3b7b2c93b4ed 100644
--- a/app/views/search/results/_wiki_blob.html.haml
+++ b/app/views/search/results/_wiki_blob.html.haml
@@ -1,9 +1,9 @@
.blob-result
.file-holder
.file-title
- = link_to project_wiki_path(@project, wiki_blob.filename) do
+ = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.filename) do
%i.fa.fa-file
%strong
= wiki_blob.filename
.file-content.code.term
- = render 'shared/file_hljs', blob: wiki_blob, first_line_number: wiki_blob.startline
+ = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline, user_color_scheme_class: 'white'
diff --git a/app/views/shared/_choose_group_avatar_button.html.haml b/app/views/shared/_choose_group_avatar_button.html.haml
index f32c2d388a7b6446c07437665127ad01e5fbf99a..000532b1c9a8f8ae71a271345c35eb28bd36f34b 100644
--- a/app/views/shared/_choose_group_avatar_button.html.haml
+++ b/app/views/shared/_choose_group_avatar_button.html.haml
@@ -1,7 +1,7 @@
-%a.choose-btn.btn.btn-small.js-choose-group-avatar-button
+%a.choose-btn.btn.btn-sm.js-choose-group-avatar-button
%i.fa.fa-paperclip
%span Choose File ...
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: 'js-group-avatar-input hidden'
-.light The maximum file size allowed is 100KB.
+.light The maximum file size allowed is 200KB.
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 1cc6043f56bb73a9dba9d6491cbd061d0342e892..a1121750ca349e0b25ba94fdf77a228385660789 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,8 +1,20 @@
- project = project || @project
.git-clone-holder.input-group
.input-group-btn
- %button{class: "btn #{ 'active' if default_clone_protocol == 'ssh' }", :"data-clone" => project.ssh_url_to_repo} SSH
- %button{class: "btn #{ 'active' if default_clone_protocol == 'http' }", :"data-clone" => project.http_url_to_repo}= gitlab_config.protocol.upcase
+ %button{ |
+ class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
+ :"data-clone" => project.ssh_url_to_repo, |
+ :"data-title" => "Add an SSH key to your profile
to pull or push via SSH",
+ :"data-html" => "true",
+ :"data-container" => "body"}
+ SSH
+ %button{ |
+ class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
+ :"data-clone" => project.http_url_to_repo, |
+ :"data-title" => "Set a password on your account
to pull or push via #{gitlab_config.protocol.upcase}",
+ :"data-html" => "true",
+ :"data-container" => "body"}
+ = gitlab_config.protocol.upcase
= text_field_tag :project_clone, default_url_to_repo(project), class: "one_click_select form-control", readonly: true
- if project.kind_of?(Project)
.input-group-addon
diff --git a/app/views/shared/_commit_message_replace_container.html.haml b/app/views/shared/_commit_message_replace_container.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..99097b884755c34f461faa7bc307e13e0d73c092
--- /dev/null
+++ b/app/views/shared/_commit_message_replace_container.html.haml
@@ -0,0 +1,14 @@
+.form-group.commit_message-group
+ = label_tag 'commit_message_replace', class: 'control-label' do
+ Commit message
+ .col-sm-10
+ .commit-message-replace-container
+ .max-width-marker
+ = text_area_tag 'commit_message_replace',
+ (params[:commit_message_replace] || local_assigns[:text]),
+ class: 'form-control', placeholder: local_assigns[:placeholder],
+ required: true, rows: (local_assigns[:rows] || 3)
+ - if local_assigns[:hint]
+ %p.hint
+ Try to keep the first line under 52 characters
+ and the others under 72.
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index ee0b57fbe5afc2f3a7d9eb71cc51e044bfdf1368..d07a9e2b92468860f9b77d3345b27dc6fff5e97c 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,5 +1,19 @@
-.event_filter
+%ul.nav.nav-pills.event_filter
= event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
+
+ - if current_user
+ - if current_controller?(:dashboard)
+ %li.pull-right
+ = link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
+ %i.fa.fa-rss
+ News Feed
+
+ - if current_controller?(:groups)
+ %li.pull-right
+ = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
+ %i.fa.fa-rss
+ News Feed
+%hr
diff --git a/app/views/shared/_file_hljs.html.haml b/app/views/shared/_file_highlight.html.haml
similarity index 66%
rename from app/views/shared/_file_hljs.html.haml
rename to app/views/shared/_file_highlight.html.haml
index 444c948b0267e95f38d13df48792e5006a9a84e7..fba69dd0f3fb2974adb6d3ad2e9e2b9002f785b2 100644
--- a/app/views/shared/_file_hljs.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,4 +1,4 @@
-%div.highlighted-data{class: user_color_scheme_class}
+.file-content.code{class: user_color_scheme_class}
.line-numbers
- if blob.data.present?
- blob.data.lines.to_a.size.times do |index|
@@ -7,7 +7,5 @@
= link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do
%i.fa.fa-link
= i
- .highlight
- %pre
- %code{ class: highlightjs_class(blob.name) }
- #{blob.data}
+ :preserve
+ #{highlight(blob.name, blob.data)}
diff --git a/app/views/shared/_filter.html.haml b/app/views/shared/_filter.html.haml
deleted file mode 100644
index d366dd97a71b4a4cce80977f18e219d1e2ba12e3..0000000000000000000000000000000000000000
--- a/app/views/shared/_filter.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-.side-filters
- = form_tag filter_path(entity), method: 'get' do
- - if current_user
- %fieldset.scope-filter
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:scope] == 'assigned-to-me')}
- = link_to filter_path(entity, scope: 'assigned-to-me') do
- Assigned to me
- %span.pull-right
- = assigned_entities_count(current_user, entity, @group)
- %li{class: ("active" if params[:scope] == 'authored')}
- = link_to filter_path(entity, scope: 'authored') do
- Created by me
- %span.pull-right
- = authored_entities_count(current_user, entity, @group)
- %li{class: ("active" if params[:scope] == 'all')}
- = link_to filter_path(entity, scope: 'all') do
- Everyone's
- %span.pull-right
- = authorized_entities_count(current_user, entity, @group)
-
- %fieldset.status-filter
- %legend State
- %ul.nav.nav-pills
- %li{class: ("active" if params[:state] == 'opened')}
- = link_to filter_path(entity, state: 'opened') do
- Open
- %li{class: ("active" if params[:state] == 'closed')}
- = link_to filter_path(entity, state: 'closed') do
- Closed
- %li{class: ("active" if params[:state] == 'all')}
- = link_to filter_path(entity, state: 'all') do
- All
-
- %fieldset
- %legend Projects
- %ul.nav.nav-pills.nav-stacked.nav-small
- - @projects.each do |project|
- - unless entities_per_project(project, entity).zero?
- %li{class: ("active" if params[:project_id] == project.id.to_s)}
- = link_to filter_path(entity, project_id: project.id) do
- = project.name_with_namespace
- %small.pull-right= entities_per_project(project, entity)
-
- %fieldset
- - if params[:state].present? || params[:project_id].present?
- = link_to filter_path(entity, state: nil, project_id: nil), class: 'pull-right cgray' do
- %i.fa.fa-times
- %strong Clear filter
-
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 93294e4250541d4e4fd13e4873b69b33424bd794..c0a9923348e72b49d8d1bd6d18f177fd06d2ea5b 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -1,9 +1,26 @@
+- if @group.persisted?
+ .form-group
+ = f.label :name, class: 'control-label' do
+ Group name
+ .col-sm-10
+ = f.text_field :name, placeholder: 'open-source', class: 'form-control'
+
.form-group
- = f.label :name, class: 'control-label' do
- Group name
+ = f.label :path, class: 'control-label' do
+ Group path
.col-sm-10
- = f.text_field :name, placeholder: 'Example Group', class: 'form-control',
- autofocus: local_assigns[:autofocus] || false
+ .input-group
+ .input-group-addon
+ = root_url
+ = f.text_field :path, placeholder: 'open-source', class: 'form-control',
+ autofocus: local_assigns[:autofocus] || false
+ - if @group.persisted?
+ .alert.alert-warning.prepend-top-10
+ %ul
+ %li Changing group path can have unintended side effects.
+ %li Renaming group path will rename directory for all related projects
+ %li It will change web url for access group and group projects.
+ %li It will change the git path to repositories under this group.
.form-group.group-description-holder
= f.label :description, 'Details', class: 'control-label'
diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..f169733f2e96ac1da31c522c64d690710147dd0d
--- /dev/null
+++ b/app/views/shared/_issuable_filter.html.haml
@@ -0,0 +1,58 @@
+.issues-filters
+ .issues-state-filters
+ %ul.nav.nav-tabs
+ %li{class: ("active" if params[:state] == 'opened')}
+ = link_to page_filter_path(state: 'opened') do
+ %i.fa.fa-exclamation-circle
+ Open
+ %li{class: ("active" if params[:state] == 'closed')}
+ = link_to page_filter_path(state: 'closed') do
+ %i.fa.fa-check-circle
+ Closed
+ %li{class: ("active" if params[:state] == 'all')}
+ = link_to page_filter_path(state: 'all') do
+ %i.fa.fa-compass
+ All
+
+ .issues-details-filters
+ = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do
+ - if controller.controller_name == 'issues'
+ .check-all-holder
+ = check_box_tag "check_all_issues", nil, false,
+ class: "check_all_issues left",
+ disabled: !can?(current_user, :modify_issue, @project)
+ .issues-other-filters
+ .filter-item.inline
+ = users_select_tag(:assignee_id, selected: params[:assignee_id],
+ placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true)
+
+ .filter-item.inline
+ = users_select_tag(:author_id, selected: params[:author_id],
+ placeholder: 'Author', class: 'trigger-submit', any_user: true)
+
+ .filter-item.inline.milestone-filter
+ = select_tag('milestone_id', projects_milestones_options, class: "select2 trigger-submit", prompt: 'Milestone')
+
+ - if @project
+ .filter-item.inline.labels-filter
+ = select_tag('label_name', project_labels_options(@project), class: "select2 trigger-submit", prompt: 'Label')
+
+ .pull-right
+ = render 'shared/sort_dropdown'
+
+ - if controller.controller_name == 'issues'
+ .issues_bulk_update.hide
+ = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
+ = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
+ = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true)
+ = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
+ = hidden_field_tag 'update[issues_ids]', []
+ = hidden_field_tag :state_event, params[:state_event]
+ = button_tag "Update issues", class: "btn update_selected_issues btn-save"
+
+:coffeescript
+ new UsersSelect()
+
+ $('form.filter-form').on 'submit', (event) ->
+ event.preventDefault()
+ Turbolinks.visit @.action + '&' + $(@).serialize()
diff --git a/app/views/shared/_issuable_search_form.html.haml b/app/views/shared/_issuable_search_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..639d203dcd69007e543e4d433301685a1796dd0c
--- /dev/null
+++ b/app/views/shared/_issuable_search_form.html.haml
@@ -0,0 +1,9 @@
+= form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
+ .append-right-10.hidden-xs.hidden-sm
+ = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
+ = hidden_field_tag :state, params['state']
+ = hidden_field_tag :scope, params['scope']
+ = hidden_field_tag :assignee_id, params['assignee_id']
+ = hidden_field_tag :author_id, params['author_id']
+ = hidden_field_tag :milestone_id, params['milestone_id']
+ = hidden_field_tag :label_id, params['label_id']
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index e976f897dc9b48b6e935d0b52d9a60061e0bd99c..0dbb6a04393720ada519a3b81d158f1a6e7f6b27 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -4,7 +4,7 @@
- project = group[0]
.panel-heading
= link_to_project project
- = link_to 'show all', project_issues_path(project), class: 'pull-right'
+ = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right'
%ul.well-list.issues-list
- group[1].each do |issue|
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index 39a1ee38f8e0371bb043d044425ef7f940948bf1..c02c5af008a35deffb3599f5c40ac886827e6744 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -4,7 +4,7 @@
- project = group[0]
.panel-heading
= link_to_project project
- = link_to 'show all', project_merge_requests_path(project), class: 'pull-right'
+ = link_to 'show all', namespace_project_merge_requests_path(project.namespace, project), class: 'pull-right'
%ul.well-list.mr-list
- group[1].each do |merge_request|
= render 'projects/merge_requests/merge_request', merge_request: merge_request
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..f685ae7726c62090cb6d495e18b24ba44edee478
--- /dev/null
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -0,0 +1,14 @@
+.milestones-filters.append-bottom-10
+ %ul.nav.nav-tabs
+ %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
+ = link_to milestones_filter_path(state: 'opened') do
+ %i.fa.fa-exclamation-circle
+ Open
+ %li{class: ("active" if params[:state] == 'closed')}
+ = link_to milestones_filter_path(state: 'closed') do
+ %i.fa.fa-check-circle
+ Closed
+ %li{class: ("active" if params[:state] == 'all')}
+ = link_to milestones_filter_path(state: 'all') do
+ %i.fa.fa-compass
+ All
diff --git a/app/views/shared/_no_password.html.haml b/app/views/shared/_no_password.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..a43bf33751a9365520535d0e569e4842528541d6
--- /dev/null
+++ b/app/views/shared/_no_password.html.haml
@@ -0,0 +1,8 @@
+- if cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && current_user.require_password?
+ .no-password-message.alert.alert-warning.hidden-xs
+ You won't be able to pull or push project code via #{gitlab_config.protocol.upcase} until you #{link_to 'set a password', edit_profile_password_path} on your account
+
+ .pull-right
+ = link_to "Don't show again", profile_path(user: {hide_no_password: true}), method: :put
+ |
+ = link_to 'Remind later', '#', class: 'hide-no-password-message'
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index e70eb4d01b99e7d6b6fb6dfa2194ba9432ea9af0..089179e677aee39c0b6d91b7859d8e4a9afb149b 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -1,14 +1,8 @@
-- if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key? && !current_user.hide_no_ssh_key
- .no-ssh-key-message
- .container
- You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
- .pull-right.hidden-xs
- = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'hide-no-ssh-message', remote: true
- |
- = link_to 'Remind later', '#', class: 'hide-no-ssh-message'
- .links-xs.visible-xs
- = link_to "Add key", new_profile_key_path
- |
- = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'hide-no-ssh-message', remote: true
- |
- = link_to 'Later', '#', class: 'hide-no-ssh-message'
+- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
+ .no-ssh-key-message.alert.alert-warning.hidden-xs
+ You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path, class: 'alert-link'} to your profile
+
+ .pull-right
+ = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link'
+ |
+ = link_to 'Remind later', '#', class: 'hide-no-ssh-message alert-link'
diff --git a/app/views/shared/_outdated_browser.html.haml b/app/views/shared/_outdated_browser.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0eba1fe075f35762dbe37f0bae29c77dbfe02db7
--- /dev/null
+++ b/app/views/shared/_outdated_browser.html.haml
@@ -0,0 +1,8 @@
+- if outdated_browser?
+ - link = "https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/requirements.md#supported-web-browsers"
+ .browser-alert
+ GitLab may not work properly because you are using an outdated web browser.
+ %br
+ Please install a
+ = link_to 'supported web browser', link
+ for a better experience.
diff --git a/app/views/shared/_project.html.haml b/app/views/shared/_project.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..8746970c239a269c3bc8723da4b8ff78be887959
--- /dev/null
+++ b/app/views/shared/_project.html.haml
@@ -0,0 +1,21 @@
+= cache [project, controller.controller_name, controller.action_name] do
+ = link_to project_path(project), class: dom_class(project) do
+ - if avatar
+ .dash-project-avatar
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+ .dash-project-access-icon
+ = visibility_level_icon(project.visibility_level)
+ %span.str-truncated
+ %span.namespace-name
+ - if project.namespace
+ = project.namespace.human_name
+ \/
+ %span.project-name.filter-title
+ = project.name
+ - if stars
+ %span.pull-right.light
+ %i.fa.fa-star
+ = project.star_count
+ - else
+ %span.arrow
+ %i.fa.fa-angle-right
diff --git a/app/views/shared/_project_filter.html.haml b/app/views/shared/_project_filter.html.haml
deleted file mode 100644
index ea6a49e1501e38b6b3dddb2d6223bcb3034f0a9a..0000000000000000000000000000000000000000
--- a/app/views/shared/_project_filter.html.haml
+++ /dev/null
@@ -1,64 +0,0 @@
-.side-filters
- = form_tag project_entities_path, method: 'get' do
- - if current_user
- %fieldset
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:scope] == 'all')}
- = link_to project_filter_path(scope: 'all') do
- Everyone's
- %span.pull-right
- = authorized_entities_count(current_user, entity, @project)
- %li{class: ("active" if params[:scope] == 'assigned-to-me')}
- = link_to project_filter_path(scope: 'assigned-to-me') do
- Assigned to me
- %span.pull-right
- = assigned_entities_count(current_user, entity, @project)
- %li{class: ("active" if params[:scope] == 'created-by-me')}
- = link_to project_filter_path(scope: 'created-by-me') do
- Created by me
- %span.pull-right
- = authored_entities_count(current_user, entity, @project)
-
- %fieldset
- %legend State
- %ul.nav.nav-pills
- %li{class: ("active" if params[:state] == 'opened')}
- = link_to project_filter_path(state: 'opened') do
- Open
- %li{class: ("active" if params[:state] == 'closed')}
- = link_to project_filter_path(state: 'closed') do
- Closed
- %li{class: ("active" if params[:state] == 'all')}
- = link_to project_filter_path(state: 'all') do
- All
-
- - if defined?(labels)
- %fieldset
- %legend
- Labels
- %small.pull-right
- = link_to project_labels_path(@project), class: 'light' do
- %i.fa.fa-pencil-square-o
- %ul.nav.nav-pills.nav-stacked.nav-small.labels-filter
- - @project.labels.order_by_name.each do |label|
- %li{class: label_filter_class(label.name)}
- = link_to labels_filter_path(label.name) do
- = render_colored_label(label)
- - if selected_label?(label.name)
- .pull-right
- %i.fa.fa-times
-
- - if @project.labels.empty?
- .light-well
- Create first label at
- = link_to 'labels page', project_labels_path(@project)
- %br
- or #{link_to 'generate', generate_project_labels_path(@project, redirect: redirect), method: :post} default set of labels
-
- %fieldset
- - if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any?
- = link_to project_entities_path, class: 'cgray pull-right' do
- %i.fa.fa-times
- %strong Clear filter
-
-
diff --git a/app/views/shared/_projects_list.html.haml b/app/views/shared/_projects_list.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4c58092af44ca17c60a75dd231dc87e5556bd179
--- /dev/null
+++ b/app/views/shared/_projects_list.html.haml
@@ -0,0 +1,17 @@
+- projects_limit = 20 unless local_assigns[:projects_limit]
+- avatar = true unless local_assigns[:avatar] == false
+- stars = false unless local_assigns[:stars] == true
+%ul.well-list.projects-list
+ - projects.each_with_index do |project, i|
+ %li{class: (i >= projects_limit) ? 'project-row hide' : 'project-row'}
+ = render "shared/project", project: project, avatar: avatar, stars: stars
+ - if projects.blank?
+ %li
+ .nothing-here-block There are no projects here.
+ - if projects.count > projects_limit
+ %li.bottom
+ %span.light
+ #{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
+ %span
+ = link_to '#', class: 'js-expand' do
+ Show all
diff --git a/app/views/shared/_promo.html.haml b/app/views/shared/_promo.html.haml
index 3400c345c4c4008688c4056b55a989e04683705d..3596aabe309d14fc23ea7015fd834436e0d5443c 100644
--- a/app/views/shared/_promo.html.haml
+++ b/app/views/shared/_promo.html.haml
@@ -1,5 +1,5 @@
.gitlab-promo
= link_to 'Homepage', promo_url
= link_to "Blog", promo_url + '/blog/'
- = link_to "@gitlabhq", "https://twitter.com/gitlabhq"
+ = link_to "@gitlab", "https://twitter.com/gitlab"
= link_to "Requests", "http://feedback.gitlab.com/"
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index 4d9534f49b1e3b2599d58007a5c07dfbf1656a17..eb2e1919e190541aae8b412ccab832b25dbfe79e 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,4 +1,4 @@
-= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
+= form_tag switch_namespace_project_refs_path(@project.namespace, @project), method: :get, class: "project-refs-form" do
= select_tag "ref", grouped_options_refs, class: "project-refs-select select2 select2-sm"
= hidden_field_tag :destination, destination
- if defined?(path)
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index 7b37b39780e50596097a9a74135039693e27ef65..af3d35de325d7465360f5764f600d5e2aa7b307c 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -1,22 +1,22 @@
.dropdown.inline.prepend-left-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
- = @sort
+ = sort_options_hash[@sort]
- else
- Newest
+ = sort_title_recently_created
%b.caret
- %ul.dropdown-menu
+ %ul.dropdown-menu.dropdown-menu-align-right
%li
- = link_to project_filter_path(sort: 'newest') do
- Newest
- = link_to project_filter_path(sort: 'oldest') do
- Oldest
- = link_to project_filter_path(sort: 'recently_updated') do
- Recently updated
- = link_to project_filter_path(sort: 'last_updated') do
- Last updated
- = link_to project_filter_path(sort: 'milestone_due_soon') do
- Milestone due soon
- = link_to project_filter_path(sort: 'milestone_due_later') do
- Milestone due later
+ = link_to page_filter_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to page_filter_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to page_filter_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to page_filter_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
+ = link_to page_filter_path(sort: sort_value_milestone_soon) do
+ = sort_title_milestone_soon
+ = link_to page_filter_path(sort: sort_value_milestone_later) do
+ = sort_title_milestone_later
diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml
index 8cec6168ab83eeb07f6c0f0efa4a56da4641e378..30458793fd13e04ec7538cb3ded4ef91cc92dde0 100644
--- a/app/views/shared/snippets/_blob.html.haml
+++ b/app/views/shared/snippets/_blob.html.haml
@@ -8,7 +8,7 @@
= render_markup(@snippet.file_name, @snippet.data)
- else
.file-content.code
- = render 'shared/file_hljs', blob: @snippet
+ = render 'shared/file_highlight', blob: @snippet
- else
.file-content.code
.nothing-here-block Empty file
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index f729f129e45f45e9b57c512047ed3ed9dda20e64..4e0663ea2080b087a3226124394d2fddf324925a 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -30,7 +30,7 @@
= f.submit 'Save', class: "btn-save btn"
- if @snippet.respond_to?(:project)
- = link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel"
- else
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index c584dd8dfb671e16e9e2c902d783c31770cde683..5bb28664349416608c23b1c75db98a4ee406f63e 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -11,7 +11,7 @@
%small.pull-right.cgray
- if snippet.project_id?
- = link_to snippet.project.name_with_namespace, project_path(snippet.project)
+ = link_to snippet.project.name_with_namespace, namespace_project_path(snippet.project.namespace, snippet.project)
.snippet-info
= "##{snippet.id}"
diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml
index b2b7ea4df0ef97d8e0b0277aa8488905f7d0cca9..0df5ade500db382bb0e31ec111f6ff37204cc6e5 100644
--- a/app/views/snippets/current_user_index.html.haml
+++ b/app/views/snippets/current_user_index.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- My Snippets
+ Your Snippets
.pull-right
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 0d71c41e2e7c502ae1a9624cece58eb28407d33b..5cd8ae26cf9c518bb647ab9876f96d8b815cade1 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -2,12 +2,12 @@
Public snippets
.pull-right
-
+
- if current_user
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet
= link_to user_snippets_path(current_user), class: "btn btn-grouped" do
- My snippets
+ Your snippets
%p.light
Public snippets created by you and other users are listed here
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index f5bc543de10a08ae19d44d79dd5ae45971bee731..55a990c94ed70320a492210777ed7008689789e5 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -23,7 +23,7 @@
.back-link
- if @snippet.author == current_user
= link_to user_snippets_path(current_user) do
- ← my snippets
+ ← your snippets
- else
= link_to snippets_path do
← discover snippets
@@ -31,13 +31,13 @@
.file-holder
.file-title
%i.fa.fa-file
- %span.file_name
+ %strong
= @snippet.file_name
- .options
+ .file-actions
.btn-group
- if can?(current_user, :modify_personal_snippet, @snippet)
- = link_to "edit", edit_snippet_path(@snippet), class: "btn btn-small", title: 'Edit Snippet'
- = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-small", target: "_blank"
+ = link_to "edit", edit_snippet_path(@snippet), class: "btn btn-sm", title: 'Edit Snippet'
+ = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
- if can?(current_user, :admin_personal_snippet, @snippet)
- = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-small btn-remove", title: 'Delete Snippet'
+ = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
= render 'shared/snippets/blob'
diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml
index 67f3a68aa22a614e127f85e6700469212bfb7ee5..df524cd18b0cb46b01612a3c6976605dccc336ba 100644
--- a/app/views/snippets/user_index.html.haml
+++ b/app/views/snippets/user_index.html.haml
@@ -5,7 +5,7 @@
\/
Snippets
- if current_user
- = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do
+ = link_to new_snippet_path, class: "btn btn-sm add_new pull-right", title: "New Snippet" do
Add new snippet
%hr
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index ea008c2dede73bc3a0a836c9557e57eb45ab5de7..f360fbb3d5d94159871f64e52cf0b9dae86a4886 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,3 +1,4 @@
-- groups.each do |group|
- = link_to group, class: 'profile-groups-avatars', :title => group.name do
- - image_tag group_icon(group.path)
+.clearfix
+ - groups.each do |group|
+ = link_to group, class: 'profile-groups-avatars inline', title: group.name do
+ = image_tag group_icon(group), class: 'avatar group-avatar s40'
diff --git a/app/views/users/_profile.html.haml b/app/views/users/_profile.html.haml
index 3b44959baad85de205a8e5af1445ae9e06b57678..bca71444956a9b4ad591ff60bb0f059b3ec2b9b9 100644
--- a/app/views/users/_profile.html.haml
+++ b/app/views/users/_profile.html.haml
@@ -12,7 +12,7 @@
- unless user.linkedin.blank?
%li
%span.light LinkedIn:
- %strong= user.linkedin
+ %strong= link_to user.linkedin, "http://www.linkedin.com/in/#{user.linkedin}"
- unless user.twitter.blank?
%li
%span.light Twitter:
@@ -21,7 +21,7 @@
%li
%span.light Website:
%strong= link_to user.short_website_url, user.full_website_url
- - unless user.bio.blank?
+ - unless user.location.blank?
%li
- %span.light Bio:
- %span= user.bio
+ %span.light Location:
+ %strong= user.location
diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml
index 1d38f8e8ab8b0838fe723b793feac72d8a1af8e3..b7383d5594e10d8b82b6d961751182a7ed954055 100644
--- a/app/views/users/_projects.html.haml
+++ b/app/views/users/_projects.html.haml
@@ -1,6 +1,13 @@
-.panel.panel-default
- .panel-heading Personal projects
- %ul.well-list
- - projects.each do |project|
- %li
- = link_to_project project
+- if @contributed_projects.present?
+ .panel.panel-default.contributed-projects
+ .panel-heading Projects contributed to
+ = render 'shared/projects_list',
+ projects: @contributed_projects.sort_by(&:star_count).reverse,
+ projects_limit: 5, stars: true, avatar: false
+
+- if @projects.present?
+ .panel.panel-default
+ .panel-heading Personal projects
+ = render 'shared/projects_list',
+ projects: @projects.sort_by(&:star_count).reverse,
+ projects_limit: 10, stars: true, avatar: false
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..922b0c6cebf1cb1425ec056195bc15025cf434d2
--- /dev/null
+++ b/app/views/users/calendar.html.haml
@@ -0,0 +1,12 @@
+%h4
+ Contributions calendar
+ .pull-right
+ %small Issues, merge requests and push events
+#cal-heatmap.calendar
+ :javascript
+ new Calendar(
+ #{@timestamps.to_json},
+ #{@starting_year},
+ #{@starting_month},
+ '#{user_calendar_activities_path}'
+ );
diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..027a93a75fcd9ba317e1dc0b44c9e1b3035b1321
--- /dev/null
+++ b/app/views/users/calendar_activities.html.haml
@@ -0,0 +1,23 @@
+%h4.prepend-top-20
+ %span.light Contributions for
+ %strong #{@calendar_date.to_s(:short)}
+
+%ul.bordered-list
+ - @events.sort_by(&:created_at).each do |event|
+ %li
+ %span.light
+ %i.fa.fa-clock-o
+ = event.created_at.to_s(:time)
+ - if event.push?
+ #{event.action_name} #{event.ref_type} #{event.ref_name}
+ - else
+ = event_action_name(event)
+ - if event.target
+ %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target]
+
+ at
+ %strong
+ - if event.project
+ = link_to_project event.project
+ - else
+ = event.project_name
diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder
new file mode 100644
index 0000000000000000000000000000000000000000..8fe30b23635fd58e414485a3a3510c36b135effe
--- /dev/null
+++ b/app/views/users/show.atom.builder
@@ -0,0 +1,12 @@
+xml.instruct!
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
+ xml.title "Activity feed for #{@user.name}"
+ xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
+ xml.link href: user_url(@user), rel: "alternate", type: "text/html"
+ xml.id projects_url
+ xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+
+ @events.each do |event|
+ event_to_atom(xml, event)
+ end
+end
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index cb49c030af223ca3c308120596bc534d60d3e362..5e1d65e2ed836fc1a9fa042c7906fab860beeb33 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,26 +1,50 @@
.row
- .col-md-8
- %h3.page-title
- = image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: ''
- = @user.name
- - if @user == current_user
- .pull-right
- = link_to profile_path, class: 'btn' do
- %i.fa.fa-pencil-square-o
- Edit Profile settings
- %br
- %span.user-show-username #{@user.username}
- %br
- %small member since #{@user.created_at.stamp("Nov 12, 2031")}
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
+ %section.col-md-8
+ .header-with-avatar
+ = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
+ %h3
+ = @user.name
+ - if @user == current_user
+ .pull-right
+ = link_to profile_path, class: 'btn btn-sm' do
+ %i.fa.fa-pencil-square-o
+ Edit Profile settings
+ .username
+ @#{@user.username}
+ .description
+ - if @user.bio.present?
+ = @user.bio
+
.clearfix
- if @groups.any?
- %h4 Groups:
- = render 'groups', groups: @groups
+ .prepend-top-20
+ %h4 Groups
+ = render 'groups', groups: @groups
+ %hr
+
+ .hidden-xs
+ .user-calendar
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ .user-calendar-activities
%hr
- %h4 User Activity:
- = render @events
- .col-md-4
+ %h4
+ User Activity
+
+ - if current_user
+ %span.rss-icon.pull-right
+ = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do
+ %strong
+ %i.fa.fa-rss
+
+ .content_list
+ = spinner
+ %aside.col-md-4
= render 'profile', user: @user
- - if @projects.present?
- = render 'projects', projects: @projects
+ = render 'projects'
+
+:coffeescript
+ $(".user-calendar").load("#{user_calendar_path}")
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index 788d9065a7b43c26062b408a4aba9b65884395ee..36ea67420641ee8af1d63bf450107dc4e56f00ba 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -1,6 +1,10 @@
.votes.votes-block
- .progress
- .progress-bar.progress-bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
- .progress-bar.progress-bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
- .upvotes= "#{votable.upvotes} up"
- .downvotes= "#{votable.downvotes} down"
+ .btn-group
+ - unless votable.upvotes.zero?
+ .btn.btn-sm.disabled.cgreen
+ %i.fa.fa-thumbs-up
+ = votable.upvotes
+ - unless votable.downvotes.zero?
+ .btn.btn-sm.disabled.cred
+ %i.fa.fa-thumbs-down
+ = votable.downvotes
diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml
index ee80547483020630cd2edf93a4e423846c99295d..2cb3ae04e1a83b670b824993ee5a59e1dcf29684 100644
--- a/app/views/votes/_votes_inline.html.haml
+++ b/app/views/votes/_votes_inline.html.haml
@@ -1,9 +1,9 @@
.votes.votes-inline
- unless votable.upvotes.zero?
- .upvotes
+ %span.upvotes.cgreen
+ #{votable.upvotes}
- unless votable.downvotes.zero?
\/
- unless votable.downvotes.zero?
- .downvotes
+ %span.downvotes.cred
\- #{votable.downvotes}
diff --git a/app/workers/auto_merge_worker.rb b/app/workers/auto_merge_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a6dd73eee5f81c8cff509d63b2294c0b05ae26a0
--- /dev/null
+++ b/app/workers/auto_merge_worker.rb
@@ -0,0 +1,13 @@
+class AutoMergeWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :default
+
+ def perform(merge_request_id, current_user_id, params)
+ params = params.with_indifferent_access
+ current_user = User.find(current_user_id)
+ merge_request = MergeRequest.find(merge_request_id)
+ merge_request.should_remove_source_branch = params[:should_remove_source_branch]
+ merge_request.automerge!(current_user, params[:commit_message])
+ end
+end
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index e3f6f3a6aefa46056bed0b98351c73942da61889..1d21addece6df43d48a79a6c0e5d478c47e06ef3 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,25 +1,58 @@
class EmailsOnPushWorker
include Sidekiq::Worker
- def perform(project_id, recipients, push_data)
+ def perform(project_id, recipients, push_data, options = {})
+ options.symbolize_keys!
+ options.reverse_merge!(
+ send_from_committer_email: false,
+ disable_diffs: false
+ )
+ send_from_committer_email = options[:send_from_committer_email]
+ disable_diffs = options[:disable_diffs]
+
project = Project.find(project_id)
before_sha = push_data["before"]
after_sha = push_data["after"]
- branch = push_data["ref"]
+ ref = push_data["ref"]
author_id = push_data["user_id"]
- if before_sha =~ /^000000/ || after_sha =~ /^000000/
- # skip if new branch was pushed or branch was removed
- return true
- end
+ action =
+ if Gitlab::Git.blank_ref?(before_sha)
+ :create
+ elsif Gitlab::Git.blank_ref?(after_sha)
+ :delete
+ else
+ :push
+ end
+
+ compare = nil
+ reverse_compare = false
+ if action == :push
+ compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
- compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
+ return false if compare.same
- # Do not send emails if git compare failed
- return false unless compare && compare.commits.present?
+ if compare.commits.empty?
+ compare = Gitlab::Git::Compare.new(project.repository.raw_repository, after_sha, before_sha)
+
+ reverse_compare = true
+
+ return false if compare.commits.empty?
+ end
+ end
recipients.split(" ").each do |recipient|
- Notify.repository_push_email(project_id, recipient, author_id, branch, compare).deliver
+ Notify.repository_push_email(
+ project_id,
+ recipient,
+ author_id: author_id,
+ ref: ref,
+ action: action,
+ compare: compare,
+ reverse_compare: reverse_compare,
+ send_from_committer_email: send_from_committer_email,
+ disable_diffs: disable_diffs
+ ).deliver
end
ensure
compare = nil
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8b50f423984f8f452b0c8ada50d6250143a6d7fb
--- /dev/null
+++ b/app/workers/irker_worker.rb
@@ -0,0 +1,169 @@
+require 'json'
+require 'socket'
+
+class IrkerWorker
+ include Sidekiq::Worker
+
+ def perform(project_id, chans, colors, push_data, settings)
+ project = Project.find(project_id)
+
+ # Get config parameters
+ return false unless init_perform settings, chans, colors
+
+ repo_name = push_data['repository']['name']
+ committer = push_data['user_name']
+ branch = push_data['ref'].gsub(%r'refs/[^/]*/', '')
+
+ if @colors
+ repo_name = "\x0304#{repo_name}\x0f"
+ branch = "\x0305#{branch}\x0f"
+ end
+
+ # Firsts messages are for branch creation/deletion
+ send_branch_updates push_data, project, repo_name, committer, branch
+
+ # Next messages are for commits
+ send_commits push_data, project, repo_name, committer, branch
+
+ close_connection
+ true
+ end
+
+ private
+
+ def init_perform(set, chans, colors)
+ @colors = colors
+ @channels = chans
+ start_connection set['server_ip'], set['server_port']
+ end
+
+ def start_connection(irker_server, irker_port)
+ begin
+ @socket = TCPSocket.new irker_server, irker_port
+ rescue Errno::ECONNREFUSED => e
+ logger.fatal "Can't connect to Irker daemon: #{e}"
+ return false
+ end
+ true
+ end
+
+ def sendtoirker(privmsg)
+ to_send = { to: @channels, privmsg: privmsg }
+ @socket.puts JSON.dump(to_send)
+ end
+
+ def close_connection
+ @socket.close
+ end
+
+ def send_branch_updates(push_data, project, repo_name, committer, branch)
+ if Gitlab::Git.blank_ref?(push_data['before'])
+ send_new_branch project, repo_name, committer, branch
+ elsif Gitlab::Git.blank_ref?(push_data['after'])
+ send_del_branch repo_name, committer, branch
+ end
+ end
+
+ def send_new_branch(project, repo_name, committer, branch)
+ repo_path = project.path_with_namespace
+ newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
+ newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
+
+ privmsg = "[#{repo_name}] #{committer} has created a new branch "
+ privmsg += "#{branch}: #{newbranch}"
+ sendtoirker privmsg
+ end
+
+ def send_del_branch(repo_name, committer, branch)
+ privmsg = "[#{repo_name}] #{committer} has deleted the branch #{branch}"
+ sendtoirker privmsg
+ end
+
+ def send_commits(push_data, project, repo_name, committer, branch)
+ return if push_data['total_commits_count'] == 0
+
+ # Next message is for number of commit pushed, if any
+ if Gitlab::Git.blank_ref?(push_data['before'])
+ # Tweak on push_data["before"] in order to have a nice compare URL
+ push_data['before'] = before_on_new_branch push_data, project
+ end
+
+ send_commits_count(push_data, project, repo_name, committer, branch)
+
+ # One message per commit, limited by 3 messages (same limit as the
+ # github irc hook)
+ commits = push_data['commits'].first(3)
+ commits.each do |hook_attrs|
+ send_one_commit project, hook_attrs, repo_name, branch
+ end
+ end
+
+ def before_on_new_branch(push_data, project)
+ commit = commit_from_id project, push_data['commits'][0]['id']
+ parents = commit.parents
+ # Return old value if there's no new one
+ return push_data['before'] if parents.empty?
+ # Or return the first parent-commit
+ parents[0].id
+ end
+
+ def send_commits_count(data, project, repo, committer, branch)
+ url = compare_url data, project.path_with_namespace
+ commits = colorize_commits data['total_commits_count']
+
+ new_commits = 'new commit'
+ new_commits += 's' if data['total_commits_count'] > 1
+
+ sendtoirker "[#{repo}] #{committer} pushed #{commits} #{new_commits} " \
+ "to #{branch}: #{url}"
+ end
+
+ def compare_url(data, repo_path)
+ sha1 = Commit::truncate_sha(data['before'])
+ sha2 = Commit::truncate_sha(data['after'])
+ compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare"
+ compare_url += "/#{sha1}...#{sha2}"
+ colorize_url compare_url
+ end
+
+ def send_one_commit(project, hook_attrs, repo_name, branch)
+ commit = commit_from_id project, hook_attrs['id']
+ sha = colorize_sha Commit::truncate_sha(hook_attrs['id'])
+ author = hook_attrs['author']['name']
+ files = colorize_nb_files(files_count commit)
+ title = commit.title
+
+ sendtoirker "#{repo_name}/#{branch} #{sha} #{author} (#{files}): #{title}"
+ end
+
+ def commit_from_id(project, id)
+ commit = Gitlab::Git::Commit.find(project.repository, id)
+ Commit.new(commit)
+ end
+
+ def files_count(commit)
+ files = "#{commit.diffs.count} file"
+ files += 's' if commit.diffs.count > 1
+ files
+ end
+
+ def colorize_sha(sha)
+ sha = "\x0314#{sha}\x0f" if @colors
+ sha
+ end
+
+ def colorize_nb_files(nb_files)
+ nb_files = "\x0312#{nb_files}\x0f" if @colors
+ nb_files
+ end
+
+ def colorize_url(url)
+ url = "\x0302\x1f#{url}\x0f" if @colors
+ url
+ end
+
+ def colorize_commits(commits)
+ commits = "\x02#{commits}\x0f" if @colors
+ commits
+ end
+end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 1406cba2db3210f4c9cb4d6b6e7b80e912f6bd7b..0c3ee6ba4ffa5a3c76a892c1e1e69919c88e43b2 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -21,7 +21,9 @@ class PostReceive
return false
end
- changes = changes.lines if changes.kind_of?(String)
+ changes = Base64.decode64(changes) unless changes.include?(" ")
+ changes = utf8_encode_changes(changes)
+ changes = changes.lines
changes.each do |change|
oldrev, newrev, ref = change.strip.split(' ')
@@ -33,7 +35,7 @@ class PostReceive
return false
end
- if tag?(ref)
+ if Gitlab::Git.tag_ref?(ref)
GitTagPushService.new.execute(project, @user, oldrev, newrev, ref)
else
GitPushService.new.execute(project, @user, oldrev, newrev, ref)
@@ -41,13 +43,20 @@ class PostReceive
end
end
- def log(message)
- Gitlab::GitLogger.error("POST-RECEIVE: #{message}")
- end
+ def utf8_encode_changes(changes)
+ changes = changes.dup
+
+ changes.force_encoding("UTF-8")
+ return changes if changes.valid_encoding?
- private
+ # Convert non-UTF-8 branch/tag names to UTF-8 so they can be dumped as JSON.
+ detection = CharlockHolmes::EncodingDetector.detect(changes)
+ return changes unless detection && detection[:encoding]
- def tag?(ref)
- !!(/refs\/tags\/(.*)/.match(ref))
+ CharlockHolmes::Converter.convert(changes, detection[:encoding], 'UTF-8')
+ end
+
+ def log(message)
+ Gitlab::GitLogger.error("POST-RECEIVE: #{message}")
end
end
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..64d39c4d3f7ef436cb4ee49e2a2d4d9a5a5ee72a
--- /dev/null
+++ b/app/workers/project_service_worker.rb
@@ -0,0 +1,10 @@
+class ProjectServiceWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :project_web_hook
+
+ def perform(hook_id, data)
+ data = data.with_indifferent_access
+ Service.find(hook_id).execute(data)
+ end
+end
diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb
index 9f9b9b1df5f672249af8397439d2c3eccc2f25c2..73085c046bd6ab50ab3183a7178ab196939f3ad9 100644
--- a/app/workers/project_web_hook_worker.rb
+++ b/app/workers/project_web_hook_worker.rb
@@ -4,6 +4,7 @@ class ProjectWebHookWorker
sidekiq_options queue: :project_web_hook
def perform(hook_id, data)
- WebHook.find(hook_id).execute data
+ data = data.with_indifferent_access
+ WebHook.find(hook_id).execute(data)
end
end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 01586150cd2c2974b0db6a473a0f6c8276dc6be7..437640d2305d1a65c9fc6038b62fd4546108a838 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -6,17 +6,27 @@ class RepositoryImportWorker
def perform(project_id)
project = Project.find(project_id)
- result = gitlab_shell.send(:import_repository,
+
+ import_result = gitlab_shell.send(:import_repository,
project.path_with_namespace,
project.import_url)
+ return project.import_fail unless import_result
+
+ data_import_result = if project.import_type == 'github'
+ Gitlab::GithubImport::Importer.new(project).execute
+ elsif project.import_type == 'gitlab'
+ Gitlab::GitlabImport::Importer.new(project).execute
+ elsif project.import_type == 'bitbucket'
+ Gitlab::BitbucketImport::Importer.new(project).execute
+ else
+ true
+ end
+ return project.import_fail unless data_import_result
- if result
- project.import_finish
- project.save
- project.satellite.create unless project.satellite.exists?
- project.update_repository_size
- else
- project.import_fail
- end
+ project.import_finish
+ project.save
+ project.satellite.create unless project.satellite.exists?
+ project.update_repository_size
+ Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
end
end
diff --git a/bin/guard b/bin/guard
new file mode 100755
index 0000000000000000000000000000000000000000..0c1a532bd01d7e797faaba230577d39c57f31208
--- /dev/null
+++ b/bin/guard
@@ -0,0 +1,16 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Bundler.
+#
+# The application 'guard' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'pathname'
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require 'rubygems'
+require 'bundler/setup'
+
+load Gem.bin_path('guard', 'guard')
diff --git a/bin/pkgr_before_precompile.sh b/bin/pkgr_before_precompile.sh
index 283abb6a0cd28d5c90f152b6f2dc137a034e4fd7..5a2007f4ab0ba511d3b3d6e9cf987709811387a1 100755
--- a/bin/pkgr_before_precompile.sh
+++ b/bin/pkgr_before_precompile.sh
@@ -18,6 +18,3 @@ rm config/resque.yml
# Set default unicorn.rb file
echo "" > config/unicorn.rb
-
-# Required for assets precompilation
-sudo service postgresql start
diff --git a/bin/rspec b/bin/rspec
index 41e37089ac299c889b86e4cfdf9d0b94151677af..20060ebd79c0ec217596c425ddc66ea58b4a02d1 100755
--- a/bin/rspec
+++ b/bin/rspec
@@ -4,4 +4,4 @@ begin
rescue LoadError
end
require 'bundler/setup'
-load Gem.bin_path('rspec', 'rspec')
+load Gem.bin_path('rspec-core', 'rspec')
diff --git a/config/application.rb b/config/application.rb
index 44a5d68d1268c67dd9538844f73f72e9acc7b9c7..fa399533e523cf81a9a77b779d5ecfac2f4697e9 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -12,11 +12,11 @@ module Gitlab
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
- config.autoload_paths += %W(#{config.root}/lib
- #{config.root}/app/models/hooks
- #{config.root}/app/models/concerns
- #{config.root}/app/models/project_services
- #{config.root}/app/models/members)
+ config.autoload_paths.push(*%W(#{config.root}/lib
+ #{config.root}/app/models/hooks
+ #{config.root}/app/models/concerns
+ #{config.root}/app/models/project_services
+ #{config.root}/app/models/members))
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
@@ -31,7 +31,7 @@ module Gitlab
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters += [:password]
+ config.filter_parameters.push(:password, :password_confirmation, :private_token)
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
@@ -50,6 +50,8 @@ module Gitlab
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
+ config.action_view.sanitized_allowed_protocols = %w(smb)
+
# Relative url support
# Uncomment and customize the last line to run in a non-root path
# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
@@ -70,7 +72,10 @@ module Gitlab
config.middleware.use Rack::Cors do
allow do
origins '*'
- resource '/api/*', headers: :any, methods: [:get, :post, :options, :put, :delete]
+ resource '/api/*',
+ headers: :any,
+ methods: [:get, :post, :options, :put, :delete],
+ expose: ['Link']
end
end
@@ -92,5 +97,8 @@ module Gitlab
redis_config_hash[:namespace] = 'cache:gitlab'
config.cache_store = :redis_store, redis_config_hash
+
+ # This is needed for gitlab-shell
+ ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH']
end
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 78bf543402b5396358facf90a23566ef0625215b..3316ece387398bf8188c9a5fb0aee12315483f50 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -11,8 +11,9 @@ Gitlab::Application.configure do
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = false
- # Compress JavaScripts and CSS
- config.assets.compress = true
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = true
@@ -74,7 +75,6 @@ Gitlab::Application.configure do
config.action_mailer.raise_delivery_errors = true
config.eager_load = true
- config.assets.js_compressor = :uglifier
config.allow_concurrency = false
end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 25b082b98da894911c20eb72c2e0ef10de41f3d4..2d5e7addcd3ebe1b6d3dc5661e962c6790415370 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -5,7 +5,7 @@ Gitlab::Application.configure do
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
- config.cache_classes = true
+ config.cache_classes = false
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_assets = true
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index bb0ffae0b708049abbb297712c47594f8999b4eb..3f1ca34a667d8f945829d28baa43fafb398fae05 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -2,6 +2,11 @@
# GitLab application config file #
# # # # # # # # # # # # # # # # # #
#
+########################### NOTE #####################################
+# This file should not receive new settings. All configuration options #
+# are being moved to ApplicationSetting model! #
+########################################################################
+#
# How to use:
# 1. Copy file as gitlab.yml
# 2. Update gitlab -> host with your fully qualified domain name
@@ -35,7 +40,7 @@ production: &base
## Date & Time settings
# Uncomment and customize if you want to change the default time zone of GitLab application.
- # To see all available zones, run `bundle exec rake time:zones:all`
+ # To see all available zones, run `bundle exec rake time:zones:all RAILS_ENV=production`
# time_zone: 'UTC'
## Email settings
@@ -43,11 +48,10 @@ production: &base
# email_enabled: true
# Email address used in the "From" field in mails sent by GitLab
email_from: example@example.com
+ email_display_name: GitLab
- # Email server smtp settings are in [a separate file](initializers/smtp_settings.rb.sample).
+ # Email server smtp settings are in config/initializers/smtp_settings.rb.sample
- ## User settings
- default_projects_limit: 10
# default_can_create_group: false # default: true
# username_changing_enabled: false # default: true - User can change her username/namespace
## Default theme
@@ -58,26 +62,12 @@ production: &base
## COLOR = 5
# default_theme: 2 # default: 2
- ## Users can create accounts
- # This also allows normal users to sign up for accounts themselves
- # default: false - By default GitLab administrators must create all new accounts
- # signup_enabled: true
-
- ## Standard login settings
- # The standard login can be disabled to force login via LDAP
- # default: true - If set to false the standard login form won't be shown on the sign-in page
- # signin_enabled: false
-
- # Restrict setting visibility levels for non-admin users.
- # The default is to allow all levels.
- # restricted_visibility_levels: [ "public" ]
-
## Automatic issue closing
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com
- # issue_closing_pattern: '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)'
+ # issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
## Default project features settings
default_projects_features:
@@ -96,35 +86,6 @@ production: &base
# The default is 'tmp/repositories' relative to the root of the Rails app.
# repository_downloads_path: tmp/repositories
- ## External issues trackers
- issues_tracker:
- # redmine:
- # title: "Redmine"
- # ## If not nil, link 'Issues' on project page will be replaced with this
- # ## Use placeholders:
- # ## :project_id - GitLab project identifier
- # ## :issues_tracker_id - Project Name or Id in external issue tracker
- # project_url: "http://redmine.sample/projects/:issues_tracker_id"
- #
- # ## If not nil, links from /#\d/ entities from commit messages will replaced with this
- # ## Use placeholders:
- # ## :project_id - GitLab project identifier
- # ## :issues_tracker_id - Project Name or Id in external issue tracker
- # ## :id - Issue id (from commit messages)
- # issues_url: "http://redmine.sample/issues/:id"
- #
- # ## If not nil, links to creating new issues will be replaced with this
- # ## Use placeholders:
- # ## :project_id - GitLab project identifier
- # ## :issues_tracker_id - Project Name or Id in external issue tracker
- # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new"
- #
- # jira:
- # title: "Atlassian Jira"
- # project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id"
- # issues_url: "http://jira.sample/browse/:id"
- # new_issue_url: "http://jira.sample/secure/CreateIssue.jspa"
-
## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
gravatar:
@@ -153,9 +114,9 @@ production: &base
label: 'LDAP'
host: '_your_ldap_server'
- port: 636
+ port: 389
uid: 'sAMAccountName'
- method: 'ssl' # "tls" or "ssl" or "plain"
+ method: 'plain' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
@@ -219,14 +180,19 @@ production: &base
# arguments, followed by optional 'args' which can be either a hash or an array.
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
providers:
- # - { name: 'google_oauth2', app_id: 'YOUR APP ID',
- # app_secret: 'YOUR APP SECRET',
+ # - { name: 'google_oauth2', app_id: 'YOUR_APP_ID',
+ # app_secret: 'YOUR_APP_SECRET',
# args: { access_type: 'offline', approval_prompt: '' } }
- # - { name: 'twitter', app_id: 'YOUR APP ID',
- # app_secret: 'YOUR APP SECRET'}
- # - { name: 'github', app_id: 'YOUR APP ID',
- # app_secret: 'YOUR APP SECRET',
+ # - { name: 'twitter', app_id: 'YOUR_APP_ID',
+ # app_secret: 'YOUR_APP_SECRET'}
+ # - { name: 'github', app_id: 'YOUR_APP_ID',
+ # app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'user:email' } }
+ # - { name: 'gitlab', app_id: 'YOUR_APP_ID',
+ # app_secret: 'YOUR_APP_SECRET',
+ # args: { scope: 'api' } }
+ # - { name: 'bitbucket', app_id: 'YOUR_APP_ID',
+ # app_secret: 'YOUR_APP_SECRET'}
@@ -293,10 +259,22 @@ production: &base
# piwik_url: '_your_piwik_url'
# piwik_site_id: '_your_piwik_site_id'
- ## Text under sign-in page (Markdown enabled)
- # sign_in_text: |
- # 
- # [Learn more about CompanyName](http://www.companydomain.com/)
+ rack_attack:
+ git_basic_auth:
+ # Rack Attack IP banning enabled
+ # enabled: true
+ #
+ # Whitelist requests from 127.0.0.1 for web proxies (NGINX/Apache) with incorrect headers
+ # ip_whitelist: ["127.0.0.1"]
+ #
+ # Limit the number of Git HTTP authentication attempts per IP
+ # maxretry: 10
+ #
+ # Reset the auth attempt counter per IP after 60 seconds
+ # findtime: 60
+ #
+ # Ban an IP for one hour (3600s) after too many auth attempts
+ # bantime: 3600
development:
<<: *base
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 27bb83784bab38955381647d98fcc6afdbc3ebf9..15c1ae9466f2cf33c43875212c48da031ee3f2b9 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -13,7 +13,11 @@ class Settings < Settingslogic
if gitlab_shell.ssh_port != 22
"ssh://#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:#{gitlab_shell.ssh_port}/"
else
- "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:"
+ if gitlab_shell.ssh_host.include? ':'
+ "[#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}]:"
+ else
+ "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:"
+ end
end
end
@@ -87,6 +91,7 @@ Settings['issues_tracker'] ||= {}
#
Settings['gitlab'] ||= Settingslogic.new({})
Settings.gitlab['default_projects_limit'] ||= 10
+Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost'
@@ -97,6 +102,7 @@ Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil?
Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}"
+Settings.gitlab['email_display_name'] ||= "GitLab"
Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git'
Settings.gitlab['user_home'] ||= begin
@@ -105,11 +111,12 @@ rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user']
end
Settings.gitlab['time_zone'] ||= nil
-Settings.gitlab['signup_enabled'] ||= false
+Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
+Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
@@ -148,7 +155,7 @@ Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.send(:build_gitlab_shell_s
Settings['backup'] ||= Settingslogic.new({})
Settings.backup['keep_time'] ||= 0
Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root)
-Settings.backup['upload'] ||= Settingslogic.new({'remote_directory' => nil, 'connection' => nil})
+Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil })
# Convert upload connection settings to use symbol keys, to make Fog happy
if Settings.backup['upload']['connection']
Settings.backup['upload']['connection'] = Hash[Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }]
@@ -171,6 +178,17 @@ Settings.satellites['timeout'] ||= 30
#
Settings['extra'] ||= Settingslogic.new({})
+#
+# Rack::Attack settings
+#
+Settings['rack_attack'] ||= Settingslogic.new({})
+Settings.rack_attack['git_basic_auth'] ||= Settingslogic.new({})
+Settings.rack_attack.git_basic_auth['enabled'] = true if Settings.rack_attack.git_basic_auth['enabled'].nil?
+Settings.rack_attack.git_basic_auth['ip_whitelist'] ||= %w{127.0.0.1}
+Settings.rack_attack.git_basic_auth['maxretry'] ||= 10
+Settings.rack_attack.git_basic_auth['findtime'] ||= 1.minute
+Settings.rack_attack.git_basic_auth['bantime'] ||= 1.hour
+
#
# Testing settings
#
diff --git a/config/initializers/4_sidekiq.rb b/config/initializers/4_sidekiq.rb
index 228b14cb5264f3cb78cbe44629d4be024e0c4318..3c930a43ad65f159f45c6a4eb85866564ddceb80 100644
--- a/config/initializers/4_sidekiq.rb
+++ b/config/initializers/4_sidekiq.rb
@@ -14,7 +14,9 @@ Sidekiq.configure_server do |config|
}
config.server_middleware do |chain|
- chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger
+ chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
+ chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MAX_RSS']
+ Disable Sidekiq arguments logging by default
end
end
diff --git a/config/initializers/6_rack_profiler.rb b/config/initializers/6_rack_profiler.rb
index a7ee3c59822ea30fe44c47ca0ac211f5b465544b..b634028756925661e59683bd908b5140275504ef 100644
--- a/config/initializers/6_rack_profiler.rb
+++ b/config/initializers/6_rack_profiler.rb
@@ -3,4 +3,6 @@ if Rails.env == 'development'
# initialization is skipped so trigger it
Rack::MiniProfilerRails.initialize!(Rails.application)
+ Rack::MiniProfiler.config.position = 'right'
+ Rack::MiniProfiler.config.start_hidden = true
end
diff --git a/config/initializers/7_omniauth.rb b/config/initializers/7_omniauth.rb
index 18759f0cfb0923bb97e4bc1013c8482f75e40875..8f6c5673103de9d4feea06b2b42be6aa37130120 100644
--- a/config/initializers/7_omniauth.rb
+++ b/config/initializers/7_omniauth.rb
@@ -9,4 +9,4 @@ if Gitlab::LDAP::Config.enabled?
server = Gitlab.config.ldap.servers.values.first
alias_method server['provider_name'], :ldap
end
-end
\ No newline at end of file
+end
diff --git a/config/initializers/acts_as_taggable_on_patch.rb b/config/initializers/acts_as_taggable_on_patch.rb
deleted file mode 100644
index baa77fde392b45754d5d94bf17079d63124125ba..0000000000000000000000000000000000000000
--- a/config/initializers/acts_as_taggable_on_patch.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-# This is a patch to address the issue in https://github.com/mbleigh/acts-as-taggable-on/issues/427 caused by
-# https://github.com/rails/rails/commit/31a43ebc107fbd50e7e62567e5208a05909ec76c
-# gem 'acts-as-taggable-on' has the fix included https://github.com/mbleigh/acts-as-taggable-on/commit/89bbed3864a9252276fb8dd7d535fce280454b90
-# but not in the currently used version of gem ('2.4.1')
-# With replacement of 'acts-as-taggable-on' gem this file will become obsolete
-
-module ActsAsTaggableOn::Taggable
- module Core
- module ClassMethods
- def tagged_with(tags, options = {})
- tag_list = ActsAsTaggableOn::TagList.from(tags)
- empty_result = where("1 = 0")
-
- return empty_result if tag_list.empty?
-
- joins = []
- conditions = []
- having = []
- select_clause = []
-
- context = options.delete(:on)
- owned_by = options.delete(:owned_by)
- alias_base_name = undecorated_table_name.gsub('.','_')
- quote = ActsAsTaggableOn::Tag.using_postgresql? ? '"' : ''
-
- if options.delete(:exclude)
- if options.delete(:wild)
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ? ESCAPE '!'", "%#{escape_like(t)}%"]) }.join(" OR ")
- else
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ?", t]) }.join(" OR ")
- end
-
- conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)})"
-
- if owned_by
- joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
- " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{owned_by.id}" +
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
- end
-
- elsif options.delete(:any)
- # get tags, drop out if nothing returned (we need at least one)
- tags = if options.delete(:wild)
- ActsAsTaggableOn::Tag.named_like_any(tag_list)
- else
- ActsAsTaggableOn::Tag.named_any(tag_list)
- end
-
- return empty_result unless tags.length > 0
-
- # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
- # avoid ambiguous column name
- taggings_context = context ? "_#{context}" : ''
-
- taggings_alias = adjust_taggings_alias(
- "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{sha_prefix(tags.map(&:name).join('_'))}"
- )
-
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
-
- # don't need to sanitize sql, map all ids and join with OR logic
- conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{t.id}" }.join(" OR ")
- select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?
-
- if owned_by
- tagging_join << " AND " +
- sanitize_sql([
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
- owned_by.id,
- owned_by.class.base_class.to_s
- ])
- end
-
- joins << tagging_join
- else
- tags = ActsAsTaggableOn::Tag.named_any(tag_list)
-
- return empty_result unless tags.length == tag_list.length
-
- tags.each do |tag|
- taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
- " AND #{taggings_alias}.tag_id = #{tag.id}"
-
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
-
- if owned_by
- tagging_join << " AND " +
- sanitize_sql([
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
- owned_by.id,
- owned_by.class.base_class.to_s
- ])
- end
-
- joins << tagging_join
- end
- end
-
- taggings_alias, tags_alias = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
-
- if options.delete(:match_all)
- joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
-
-
- group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
- group = group_columns
- having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
- end
-
- select(select_clause) \
- .joins(joins.join(" ")) \
- .where(conditions.join(" AND ")) \
- .group(group) \
- .having(having) \
- .order(options[:order]) \
- .readonly(false)
- end
- end
- end
-end
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
index d0065b63e545ead2c3cf7d762b673f5ce495471a..bfb8656df552da088bf7848deaa77540945bead2 100644
--- a/config/initializers/carrierwave.rb
+++ b/config/initializers/carrierwave.rb
@@ -12,22 +12,30 @@ if File.exists?(aws_file)
aws_secret_access_key: AWS_CONFIG['secret_access_key'], # required
region: AWS_CONFIG['region'], # optional, defaults to 'us-east-1'
}
- config.fog_directory = AWS_CONFIG['bucket'] # required
- config.fog_public = false # optional, defaults to true
- config.fog_attributes = {'Cache-Control'=>'max-age=315576000'} # optional, defaults to {}
- config.fog_authenticated_url_expiration = 1 << 29 # optional time (in seconds) that authenticated urls will be valid.
- # when fog_public is false and provider is AWS or Google, defaults to 600
+
+ # required
+ config.fog_directory = AWS_CONFIG['bucket']
+
+ # optional, defaults to true
+ config.fog_public = false
+
+ # optional, defaults to {}
+ config.fog_attributes = { 'Cache-Control'=>'max-age=315576000' }
+
+ # optional time (in seconds) that authenticated urls will be valid.
+ # when fog_public is false and provider is AWS or Google, defaults to 600
+ config.fog_authenticated_url_expiration = 1 << 29
end
# Mocking Fog requests, based on: https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Test-Fog-based-uploaders
if Rails.env.test?
Fog.mock!
connection = ::Fog::Storage.new(
- :aws_access_key_id => AWS_CONFIG['access_key_id'],
- :aws_secret_access_key => AWS_CONFIG['secret_access_key'],
- :provider => 'AWS',
- :region => AWS_CONFIG['region']
+ aws_access_key_id: AWS_CONFIG['access_key_id'],
+ aws_secret_access_key: AWS_CONFIG['secret_access_key'],
+ provider: 'AWS',
+ region: AWS_CONFIG['region']
)
- connection.directories.create(:key => AWS_CONFIG['bucket'])
+ connection.directories.create(key: AWS_CONFIG['bucket'])
end
end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index c6eb3e510362bbf9b05e5f0a2a2c74f90d64e265..79abe3c695da44bc1fd840e7b87262ce217f82a2 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -145,7 +145,8 @@ Devise.setup do |config|
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
- config.reset_password_within = 2.hours
+ # When someone else invites you to GitLab this time is also used so it should be pretty long.
+ config.reset_password_within = 2.days
# ==> Configuration for :encryptable
# Allow you to use another encryption algorithm besides bcrypt (default). You can use
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9da7ebf4290df3f7a04f44c96480018332ba536c
--- /dev/null
+++ b/config/initializers/doorkeeper.rb
@@ -0,0 +1,102 @@
+Doorkeeper.configure do
+ # Change the ORM that doorkeeper will use.
+ # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
+ orm :active_record
+
+ # This block will be called to check whether the resource owner is authenticated or not.
+ resource_owner_authenticator do
+ # Put your resource owner authentication logic here.
+ # Example implementation:
+ current_user || redirect_to(new_user_session_url)
+ end
+
+ resource_owner_from_credentials do |routes|
+ u = User.find_by(email: params[:username])
+ u if u && u.valid_password?(params[:password])
+ end
+
+ # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
+ # admin_authenticator do
+ # # Put your admin authentication logic here.
+ # # Example implementation:
+ # Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
+ # end
+
+ # Authorization Code expiration time (default 10 minutes).
+ # authorization_code_expires_in 10.minutes
+
+ # Access token expiration time (default 2 hours).
+ # If you want to disable expiration, set this to nil.
+ access_token_expires_in nil
+
+ # Reuse access token for the same resource owner within an application (disabled by default)
+ # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
+ # reuse_access_token
+
+ # Issue access tokens with refresh token (disabled by default)
+ use_refresh_token
+
+ # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
+ # by default in non-development environments). OAuth2 delegates security in
+ # communication to the HTTPS protocol so it is wise to keep this enabled.
+ #
+ force_ssl_in_redirect_uri false
+
+ # Provide support for an owner to be assigned to each registered application (disabled by default)
+ # Optional parameter confirmation: true (default false) if you want to enforce ownership of
+ # a registered application
+ # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
+ enable_application_owner confirmation: false
+
+ # Define access token scopes for your provider
+ # For more information go to
+ # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
+ default_scopes :api
+ #optional_scopes :write, :update
+
+ # Change the way client credentials are retrieved from the request object.
+ # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
+ # falls back to the `:client_id` and `:client_secret` params from the `params` object.
+ # Check out the wiki for more information on customization
+ # client_credentials :from_basic, :from_params
+
+ # Change the way access token is authenticated from the request object.
+ # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
+ # falls back to the `:access_token` or `:bearer_token` params from the `params` object.
+ # Check out the wiki for more information on customization
+ access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param
+
+ # Change the native redirect uri for client apps
+ # When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
+ # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
+ # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
+ #
+ native_redirect_uri nil#'urn:ietf:wg:oauth:2.0:oob'
+
+ # Specify what grant flows are enabled in array of Strings. The valid
+ # strings and the flows they enable are:
+ #
+ # "authorization_code" => Authorization Code Grant Flow
+ # "implicit" => Implicit Grant Flow
+ # "password" => Resource Owner Password Credentials Grant Flow
+ # "client_credentials" => Client Credentials Grant Flow
+ #
+ # If not specified, Doorkeeper enables all the four grant flows.
+ #
+ # grant_flows %w(authorization_code implicit password client_credentials)
+
+ # Under some circumstances you might want to have applications auto-approved,
+ # so that the user skips the authorization step.
+ # For example if dealing with trusted a application.
+ # skip_authorization do |resource_owner, client|
+ # client.superapp? or resource_owner.admin?
+ # end
+
+ # WWW-Authenticate Realm (default "Doorkeeper").
+ # realm "Doorkeeper"
+
+ # Allow dynamic query parameters (disabled by default)
+ # Some applications require dynamic query parameters on their request_uri
+ # set to true if you want this to be allowed
+ # wildcard_redirect_uri false
+end
diff --git a/config/initializers/gitlab_shell_secret_token.rb b/config/initializers/gitlab_shell_secret_token.rb
index 8d2b771e53590b3abb9133df0475133ba4e5bf7c..e7c9f0ba7c2b8025bcc520cc307c5e05edd3a01b 100644
--- a/config/initializers/gitlab_shell_secret_token.rb
+++ b/config/initializers/gitlab_shell_secret_token.rb
@@ -16,4 +16,4 @@ end
if File.exist?(Gitlab.config.gitlab_shell.path) && !File.exist?(gitlab_shell_symlink)
FileUtils.symlink(secret_file, gitlab_shell_symlink)
-end
\ No newline at end of file
+end
diff --git a/config/initializers/public_key.rb b/config/initializers/public_key.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e4f09a2d02058d36952d667b866e2ff603942d42
--- /dev/null
+++ b/config/initializers/public_key.rb
@@ -0,0 +1,2 @@
+path = File.expand_path("~/.ssh/bitbucket_rsa.pub")
+Gitlab::BitbucketImport.public_key = File.read(path) if File.exist?(path)
diff --git a/config/initializers/rack_attack_git_basic_auth.rb b/config/initializers/rack_attack_git_basic_auth.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bbbfed6832923cb41e94d2defe0af74843bf77a4
--- /dev/null
+++ b/config/initializers/rack_attack_git_basic_auth.rb
@@ -0,0 +1,12 @@
+unless Rails.env.test?
+ # Tell the Rack::Attack Rack middleware to maintain an IP blacklist. We will
+ # update the blacklist from Grack::Auth#authenticate_user.
+ Rack::Attack.blacklist('Git HTTP Basic Auth') do |req|
+ Rack::Attack::Allow2Ban.filter(req.ip, Gitlab.config.rack_attack.git_basic_auth) do
+ # This block only gets run if the IP was not already banned.
+ # Return false, meaning that we do not see anything wrong with the
+ # request at this time
+ false
+ end
+ end
+end
diff --git a/config/initializers/redis-store-fix-expiry.rb b/config/initializers/redis-store-fix-expiry.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fce0a13533012e92babfbb1dca780ad3b84932ff
--- /dev/null
+++ b/config/initializers/redis-store-fix-expiry.rb
@@ -0,0 +1,44 @@
+# Monkey-patch Redis::Store to make 'setex' and 'expire' work with namespacing
+
+module Gitlab
+ class Redis
+ class Store
+ module Namespace
+ # Redis::Store#setex in redis-store 1.1.4 does not respect namespaces;
+ # this new method does.
+ def setex(key, expires_in, value, options=nil)
+ namespace(key) { |key| super(key, expires_in, value) }
+ end
+
+ # Redis::Store#expire in redis-store 1.1.4 does not respect namespaces;
+ # this new method does.
+ def expire(key, expires_in)
+ namespace(key) { |key| super(key, expires_in) }
+ end
+
+ private
+
+ # Our new definitions of #setex and #expire above assume that the
+ # #namespace method exists. Because we cannot be sure of that, we
+ # re-implement the #namespace method from Redis::Store::Namespace so
+ # that it is available for all Redis::Store instances, whether they use
+ # namespacing or not.
+ #
+ # Based on lib/redis/store/namespace.rb L49-51 (redis-store 1.1.4)
+ def namespace(key)
+ if @namespace
+ yield interpolate(key)
+ else
+ # This Redis::Store instance does not use a namespace so we should
+ # just pass through the key.
+ yield key
+ end
+ end
+ end
+ end
+ end
+end
+
+Redis::Store.class_eval do
+ include Gitlab::Redis::Store::Namespace
+end
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index 3711b03796e797c5dc76544b6cd9bf611b7af594..f0fe2fdfa43909e794854815b4c9c51b92d83b04 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -1,8 +1,11 @@
-# To enable smtp email delivery for your GitLab instance do next:
+# To enable smtp email delivery for your GitLab instance do the following:
# 1. Rename this file to smtp_settings.rb
# 2. Edit settings inside this file
# 3. Restart GitLab instance
#
+# For full list of options and their values see http://api.rubyonrails.org/classes/ActionMailer/Base.html
+#
+
if Rails.env.production?
Gitlab::Application.config.action_mailer.delivery_method = :smtp
@@ -13,6 +16,7 @@ if Rails.env.production?
password: "123456",
domain: "gitlab.company.com",
authentication: :login,
- enable_starttls_auto: true
+ enable_starttls_auto: true,
+ openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options
}
end
diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d9042c652bb401fa3e1cd473392ca0160747c8c9
--- /dev/null
+++ b/config/initializers/static_files.rb
@@ -0,0 +1,15 @@
+app = Rails.application
+
+if app.config.serve_static_assets
+ # The `ActionDispatch::Static` middleware intercepts requests for static files
+ # by checking if they exist in the `/public` directory.
+ # We're replacing it with our `Gitlab::Middleware::Static` that does the same,
+ # except ignoring `/uploads`, letting those go through to the GitLab Rails app.
+
+ app.config.middleware.swap(
+ ActionDispatch::Static,
+ Gitlab::Middleware::Static,
+ app.paths["public"].first,
+ app.config.static_cache_control
+ )
+end
diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml
index 1cbcde5b3da4e3549b9f35bebe85c0232a0fbdeb..f3db5b7476e1ab35221fe1e6cfd55d9be6c82492 100644
--- a/config/locales/devise.en.yml
+++ b/config/locales/devise.en.yml
@@ -23,8 +23,8 @@ en:
timeout: 'Your session expired, please sign in again to continue.'
inactive: 'Your account was not activated yet.'
sessions:
- signed_in: 'Signed in successfully.'
- signed_out: 'Signed out successfully.'
+ signed_in: ''
+ signed_out: ''
users_sessions:
user:
signed_in: 'Signed in successfully.'
@@ -57,4 +57,4 @@ en:
reset_password_instructions:
subject: 'Reset password instructions'
unlock_instructions:
- subject: 'Unlock Instructions'
+ subject: 'Unlock Instructions'
\ No newline at end of file
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c5b6b75e7f6408f07c0b7cd387dfd0675f29341c
--- /dev/null
+++ b/config/locales/doorkeeper.en.yml
@@ -0,0 +1,73 @@
+en:
+ activerecord:
+ errors:
+ models:
+ application:
+ attributes:
+ redirect_uri:
+ fragment_present: 'cannot contain a fragment.'
+ invalid_uri: 'must be a valid URI.'
+ relative_uri: 'must be an absolute URI.'
+ mongoid:
+ errors:
+ models:
+ application:
+ attributes:
+ redirect_uri:
+ fragment_present: 'cannot contain a fragment.'
+ invalid_uri: 'must be a valid URI.'
+ relative_uri: 'must be an absolute URI.'
+ mongo_mapper:
+ errors:
+ models:
+ application:
+ attributes:
+ redirect_uri:
+ fragment_present: 'cannot contain a fragment.'
+ invalid_uri: 'must be a valid URI.'
+ relative_uri: 'must be an absolute URI.'
+ doorkeeper:
+ errors:
+ messages:
+ # Common error messages
+ invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
+ invalid_redirect_uri: 'The redirect uri included is not valid.'
+ unauthorized_client: 'The client is not authorized to perform this request using this method.'
+ access_denied: 'The resource owner or authorization server denied the request.'
+ invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
+ server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
+ temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
+
+ #configuration error messages
+ credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
+ resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'
+
+ # Access grant errors
+ unsupported_response_type: 'The authorization server does not support this response type.'
+
+ # Access token errors
+ invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
+ invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
+ unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
+
+ # Password Access token errors
+ invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found'
+
+ invalid_token:
+ revoked: "The access token was revoked"
+ expired: "The access token expired"
+ unknown: "The access token is invalid"
+ scopes:
+ api: Access your API
+
+ flash:
+ applications:
+ create:
+ notice: 'Application created.'
+ destroy:
+ notice: 'Application deleted.'
+ update:
+ notice: 'Application updated.'
+ authorized_applications:
+ destroy:
+ notice: 'Application revoked.'
diff --git a/config/routes.rb b/config/routes.rb
index 2534153758bd9d7fc1a8c4bf5ec994bf8c92ac94..388858d2670ae582440b2b18762f0b7f431bf033 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,11 +2,20 @@ require 'sidekiq/web'
require 'api/api'
Gitlab::Application.routes.draw do
- #
+ use_doorkeeper do
+ controllers applications: 'oauth/applications',
+ authorized_applications: 'oauth/authorized_applications',
+ authorizations: 'oauth/authorizations'
+ end
+
+ # Autocomplete
+ get '/autocomplete/users' => 'autocomplete#users'
+ get '/autocomplete/users/:id' => 'autocomplete#user'
+
+
# Search
- #
- get 'search' => "search#show"
- get 'search/autocomplete' => "search#autocomplete", as: :search_autocomplete
+ get 'search' => 'search#show'
+ get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
# API
API::API.logger Rails.logger
@@ -15,9 +24,9 @@ Gitlab::Application.routes.draw do
# Get all keys of user
get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ }
- constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? }
+ constraint = lambda { |request| request.env['warden'].authenticate? and request.env['warden'].user.admin? }
constraints constraint do
- mount Sidekiq::Web, at: "/admin/sidekiq", as: :sidekiq
+ mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq
end
# Enable Grack support
@@ -28,26 +37,75 @@ Gitlab::Application.routes.draw do
receive_pack: Gitlab.config.gitlab_shell.receive_pack
}), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
- #
# Help
- #
-
get 'help' => 'help#index'
get 'help/:category/:file' => 'help#show', as: :help_page
get 'help/shortcuts'
+ get 'help/ui' => 'help#ui'
#
# Global snippets
#
resources :snippets do
member do
- get "raw"
+ get 'raw'
+ end
+ end
+ get '/s/:username' => 'snippets#user_index', as: :user_snippets, constraints: { username: /.*/ }
+
+
+ #
+ # Import
+ #
+ namespace :import do
+ resource :github, only: [:create, :new], controller: :github do
+ get :status
+ get :callback
+ get :jobs
+ end
+
+ resource :gitlab, only: [:create, :new], controller: :gitlab do
+ get :status
+ get :callback
+ get :jobs
+ end
+
+ resource :bitbucket, only: [:create, :new], controller: :bitbucket do
+ get :status
+ get :callback
+ get :jobs
+ end
+
+ resource :gitorious, only: [:create, :new], controller: :gitorious do
+ get :status
+ get :callback
+ get :jobs
end
end
- get "/s/:username" => "snippets#user_index", as: :user_snippets, constraints: { username: /.*/ }
#
- # Explroe area
+ # Uploads
+ #
+
+ scope path: :uploads do
+ # Note attachments and User/Group/Project avatars
+ get ":model/:mounted_as/:id/:filename",
+ to: "uploads#show",
+ constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /.+/ }
+
+ # Project markdown uploads
+ get ":namespace_id/:project_id/:secret/:filename",
+ to: "projects/uploads#show",
+ constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: /.+/ }
+ end
+
+ # Redirect old note attachments path to new uploads path.
+ get "files/note/:id/:filename",
+ to: redirect("uploads/note/attachment/%{id}/%{filename}"),
+ constraints: { filename: /.+/ }
+
+ #
+ # Explore area
#
namespace :explore do
resources :projects, only: [:index] do
@@ -58,23 +116,19 @@ Gitlab::Application.routes.draw do
end
resources :groups, only: [:index]
- root to: "projects#trending"
+ root to: 'projects#trending'
end
# Compatibility with old routing
- get 'public' => "explore/projects#index"
- get 'public/projects' => "explore/projects#index"
-
- #
- # Attachments serving
- #
- get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ }
+ get 'public' => 'explore/projects#index'
+ get 'public/projects' => 'explore/projects#index'
#
# Admin Area
#
namespace :admin do
resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do
+ resources :keys, only: [:show, :destroy]
member do
put :team_update
put :block
@@ -83,9 +137,11 @@ Gitlab::Application.routes.draw do
end
end
+ resources :applications
+
resources :groups, constraints: { id: /[^\/]+/ } do
member do
- put :project_teams_update
+ put :members_update
end
end
@@ -97,13 +153,26 @@ Gitlab::Application.routes.draw do
resource :logs, only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
- resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do
- member do
- put :transfer
+ resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
+ root to: 'projects#index', as: :projects
+
+ resources(:projects,
+ path: '/',
+ constraints: { id: /[a-zA-Z.0-9_\-]+/ },
+ only: [:index, :show]) do
+ root to: 'projects#show'
+
+ member do
+ put :transfer
+ end
end
end
- root to: "dashboard#index"
+ resource :application_settings, only: [:show, :update] do
+ resources :services
+ end
+
+ root to: 'dashboard#index'
end
#
@@ -113,13 +182,18 @@ Gitlab::Application.routes.draw do
member do
get :history
get :design
+ get :applications
put :reset_private_token
put :update_username
end
scope module: :profiles do
- resource :account, only: [:show, :update]
+ resource :account, only: [:show, :update] do
+ member do
+ delete :unlink
+ end
+ end
resource :notifications, only: [:show, :update]
resource :password, only: [:new, :create, :edit, :update] do
member do
@@ -128,43 +202,58 @@ Gitlab::Application.routes.draw do
end
resources :keys
resources :emails, only: [:index, :create, :destroy]
- resources :groups, only: [:index] do
- member do
- delete :leave
- end
- end
resource :avatar, only: [:destroy]
end
end
- match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ }, via: :get
+ get 'u/:username/calendar' => 'users#calendar', as: :user_calendar,
+ constraints: { username: /.*/ }
+
+ get 'u/:username/calendar_activities' => 'users#calendar_activities', as: :user_calendar_activities,
+ constraints: { username: /.*/ }
+
+ get '/u/:username' => 'users#show', as: :user,
+ constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
#
# Dashboard Area
#
- resource :dashboard, controller: "dashboard", only: [:show] do
+ resource :dashboard, controller: 'dashboard', only: [:show] do
member do
- get :projects
get :issues
get :merge_requests
end
+
+ scope module: :dashboard do
+ resources :milestones, only: [:index, :show]
+
+ resources :groups, only: [:index]
+
+ resources :projects, only: [] do
+ collection do
+ get :starred
+ end
+ end
+ end
end
#
# Groups Area
#
- resources :groups, constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} do
+ resources :groups, constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } do
member do
get :issues
get :merge_requests
- get :members
get :projects
end
scope module: :groups do
- resources :group_members, only: [:create, :update, :destroy]
+ resources :group_members, only: [:index, :create, :update, :destroy] do
+ delete :leave, on: :collection
+ end
+
resource :avatar, only: [:destroy]
- resources :milestones
+ resources :milestones, only: [:index, :show, :update]
end
end
@@ -173,170 +262,236 @@ Gitlab::Application.routes.draw do
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords, sessions: :sessions, confirmations: :confirmations }
devise_scope :user do
- get "/users/auth/:provider/omniauth_error" => "omniauth_callbacks#omniauth_error", as: :omniauth_error
+ get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
end
+
+ root to: "dashboard#show"
+
#
# Project Area
#
- resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do
- member do
- put :transfer
- post :fork
- post :archive
- post :unarchive
- post :upload_image
- post :toggle_star
- get :autocomplete_sources
- get :import
- put :retry_import
- end
-
- scope module: :projects do
- resources :blob, only: [:show, :destroy], constraints: { id: /.+/ } do
- get :diff, on: :member
- end
- resources :raw, only: [:show], constraints: {id: /.+/}
- resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
- resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
- post :preview, on: :member
+ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
+ resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+/ }, except:
+ [:new, :create, :index], path: "/") do
+ member do
+ put :transfer
+ post :archive
+ post :unarchive
+ post :toggle_star
+ post :markdown_preview
+ get :autocomplete_sources
end
- resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
- resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
- resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
- resources :compare, only: [:index, :create]
- resources :blame, only: [:show], constraints: {id: /.+/}
- resources :network, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
- resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/} do
- member do
- get :commits
+
+ scope module: :projects do
+ # Blob routes:
+ get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob'
+ post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob'
+ get '/edit/*id', to: 'blob#edit', constraints: { id: /.+/ }, as: 'edit_blob'
+ put '/update/*id', to: 'blob#update', constraints: { id: /.+/ }, as: 'update_blob'
+ post '/preview/*id', to: 'blob#preview', constraints: { id: /.+/ }, as: 'preview_blob'
+
+ scope do
+ get(
+ '/blob/*id/diff',
+ to: 'blob#diff',
+ constraints: { id: /.+/, format: false },
+ as: :blob_diff
+ )
+ get(
+ '/blob/*id',
+ to: 'blob#show',
+ constraints: { id: /.+/, format: false },
+ as: :blob
+ )
+ delete(
+ '/blob/*id',
+ to: 'blob#destroy',
+ constraints: { id: /.+/, format: false }
+ )
+ end
+
+ scope do
+ get(
+ '/raw/*id',
+ to: 'raw#show',
+ constraints: { id: /.+/, format: /(html|js)/ },
+ as: :raw
+ )
end
- end
- match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
+ scope do
+ get(
+ '/tree/*id',
+ to: 'tree#show',
+ constraints: { id: /.+/, format: /(html|js)/ },
+ as: :tree
+ )
+ end
+
+ scope do
+ get(
+ '/blame/*id',
+ to: 'blame#show',
+ constraints: { id: /.+/, format: /(html|js)/ },
+ as: :blame
+ )
+ end
+
+ scope do
+ get(
+ '/commits/*id',
+ to: 'commits#show',
+ constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ },
+ as: :commits
+ )
+ end
- resources :snippets, constraints: {id: /\d+/} do
+ resource :avatar, only: [:show, :destroy]
+ resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do
+ get :branches, on: :member
+ end
+
+ resources :compare, only: [:index, :create]
+ resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ }
+
+ resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do
member do
- get "raw"
+ get :commits
end
end
- resources :wikis, only: [:show, :edit, :destroy, :create], constraints: {id: /[a-zA-Z.0-9_\-\/]+/} do
- collection do
- get :pages
- put ':id' => 'wikis#update'
- get :git_access
+ get '/compare/:from...:to' => 'compare#show', :as => 'compare',
+ :constraints => { from: /.+/, to: /.+/ }
+
+ resources :snippets, constraints: { id: /\d+/ } do
+ member do
+ get 'raw'
+ end
end
- member do
- get "history"
+ resources :wikis, only: [:show, :edit, :destroy, :create], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ } do
+ collection do
+ get :pages
+ put ':id' => 'wikis#update'
+ get :git_access
+ end
+
+ member do
+ get 'history'
+ end
end
- end
- resource :repository, only: [:show] do
- member do
- get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex }
+ resource :repository, only: [:show, :create] do
+ member do
+ get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex }
+ end
end
- end
- resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
- member do
- get :test
+ resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
+ member do
+ get :test
+ end
end
- end
- resources :deploy_keys, constraints: {id: /\d+/} do
- member do
- put :enable
- put :disable
+ resources :deploy_keys, constraints: { id: /\d+/ } do
+ member do
+ put :enable
+ put :disable
+ end
end
- end
- resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
- resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
- resources :protected_branches, only: [:index, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
+ resource :fork, only: [:new, :create]
+ resource :import, only: [:new, :create, :show]
- resources :refs, only: [] do
- collection do
- get "switch"
- end
+ resources :refs, only: [] do
+ collection do
+ get 'switch'
+ end
- member do
- # tree viewer logs
- get "logs_tree", constraints: { id: Gitlab::Regex.git_reference_regex }
- get "logs_tree/:path" => "refs#logs_tree",
- as: :logs_file,
- constraints: {
- id: Gitlab::Regex.git_reference_regex,
+ member do
+ # tree viewer logs
+ get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex }
+ get 'logs_tree/:path' => 'refs#logs_tree', as: :logs_file, constraints: {
+ id: Gitlab::Regex.git_reference_regex,
path: /.*/
}
+ end
end
- end
- resources :merge_requests, constraints: {id: /\d+/}, except: [:destroy] do
- member do
- get :diffs
- post :automerge
- get :automerge_check
- get :ci_status
- end
+ resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
+ member do
+ get :diffs
+ post :automerge
+ get :automerge_check
+ get :ci_status
+ post :toggle_subscription
+ end
- collection do
- get :branch_from
- get :branch_to
- get :update_branches
+ collection do
+ get :branch_from
+ get :branch_to
+ get :update_branches
+ end
end
- end
- resources :hooks, only: [:index, :create, :destroy], constraints: {id: /\d+/} do
- member do
- get :test
+ resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
+ resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
+ resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
+
+ resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do
+ member do
+ get :test
+ end
end
- end
- resources :team, controller: 'team_members', only: [:index]
- resources :milestones, except: [:destroy], constraints: {id: /\d+/} do
- member do
- put :sort_issues
- put :sort_merge_requests
+ resources :milestones, except: [:destroy], constraints: { id: /\d+/ } do
+ member do
+ put :sort_issues
+ put :sort_merge_requests
+ end
end
- end
- resources :labels, constraints: {id: /\d+/} do
- collection do
- post :generate
+ resources :labels, constraints: { id: /\d+/ } do
+ collection do
+ post :generate
+ end
end
- end
- resources :issues, constraints: {id: /\d+/}, except: [:destroy] do
- collection do
- post :bulk_update
+ resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do
+ member do
+ post :toggle_subscription
+ end
+ collection do
+ post :bulk_update
+ end
end
- end
- resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
- collection do
- delete :leave
+ resources :project_members, except: [:new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
+ collection do
+ delete :leave
- # Used for import team
- # from another project
- get :import
- post :apply_import
+ # Used for import team
+ # from another project
+ get :import
+ post :apply_import
+ end
end
- end
- resources :notes, only: [:index, :create, :destroy, :update], constraints: {id: /\d+/} do
- member do
- delete :delete_attachment
+ resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do
+ member do
+ delete :delete_attachment
+ end
end
- collection do
- post :preview
+ resources :uploads, only: [:create] do
+ collection do
+ get ":secret/:filename", action: :show, as: :show, constraints: { filename: /.+/ }
+ end
end
end
+
end
end
- get ':id' => "namespaces#show", constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
-
- root to: "dashboard#show"
+ get ':id' => 'namespaces#show', constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
end
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index ea22744fd90c49eb4817f0c0ec8952b30df4b7ee..86a5512e761982b0cfa80edf6ed5a793dece593d 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -13,10 +13,10 @@
#
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
-# Use at least one worker per core if you're on a dedicated server,
-# more will usually help for _short_ waits on databases/caches.
-# The minimum is 2
-worker_processes 2
+# Read about unicorn workers here:
+# http://doc.gitlab.com/ee/install/requirements.html#unicorn-workers
+#
+worker_processes 3
# Since Unicorn is never exposed to outside clients, it does not need to
# run on the standard HTTP port (80), there is no reason to start Unicorn
@@ -37,10 +37,10 @@ listen "127.0.0.1:8080", :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
#
-# NOTICE: git push over http depends on this value.
-# If you want be able to push huge amount of data to git repository over http
-# you will have to increase this value too.
-#
+# NOTICE: git push over http depends on this value.
+# If you want be able to push huge amount of data to git repository over http
+# you will have to increase this value too.
+#
# Example of output if you try to push 1GB repo to GitLab over http.
# -> git push http://gitlab.... master
#
diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb
index 004d4cd64a135a2b1a776be2190e776166bc64f2..bba2fc4b186ea050f64b106928d22db3011d38ef 100644
--- a/db/fixtures/development/01_admin.rb
+++ b/db/fixtures/development/01_admin.rb
@@ -3,9 +3,9 @@ Gitlab::Seeder.quiet do
s.id = 1
s.name = 'Administrator'
s.email = 'admin@example.com'
+ s.notification_email = 'admin@example.com'
s.username = 'root'
s.password = '5iveL!fe'
- s.password_confirmation = '5iveL!fe'
s.admin = true
s.projects_limit = 100
s.confirmed_at = DateTime.now
diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb
index c263dd232aff567c190df99999bd71f12bc46bc9..24952a1f66167a2d324380de6791c34b9399535f 100644
--- a/db/fixtures/development/05_users.rb
+++ b/db/fixtures/development/05_users.rb
@@ -1,31 +1,31 @@
Gitlab::Seeder.quiet do
(2..20).each do |i|
begin
- User.seed(:id, [{
- id: i,
+ User.create!(
username: Faker::Internet.user_name,
name: Faker::Name.name,
email: Faker::Internet.email,
- confirmed_at: DateTime.now
- }])
+ confirmed_at: DateTime.now,
+ password: '12345678'
+ )
+
print '.'
- rescue ActiveRecord::RecordNotSaved
+ rescue ActiveRecord::RecordInvalid
print 'F'
end
end
(1..5).each do |i|
begin
- User.seed(:id, [
- id: i + 10,
+ User.create!(
username: "user#{i}",
name: "User #{i}",
email: "user#{i}@example.com",
confirmed_at: DateTime.now,
password: '12345678'
- ])
+ )
print '.'
- rescue ActiveRecord::RecordNotSaved
+ rescue ActiveRecord::RecordInvalid
print 'F'
end
end
diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb
index 0755ac714e11c217c395bf71d5f31cb0d0a94c55..8b560ee09e00251a28c05b13d9d972f0a0c23946 100644
--- a/db/fixtures/production/001_admin.rb
+++ b/db/fixtures/production/001_admin.rb
@@ -11,7 +11,6 @@ admin = User.create(
name: "Administrator",
username: 'root',
password: password,
- password_confirmation: password,
password_expires_at: expire_time,
theme_id: Gitlab::Theme::MARS
diff --git a/db/migrate/20140125162722_add_avatar_to_projects.rb b/db/migrate/20140125162722_add_avatar_to_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9523ac722f2fa2144759e3cd268374eb1747e322
--- /dev/null
+++ b/db/migrate/20140125162722_add_avatar_to_projects.rb
@@ -0,0 +1,5 @@
+class AddAvatarToProjects < ActiveRecord::Migration
+ def change
+ add_column :projects, :avatar, :string
+ end
+end
diff --git a/db/migrate/20140907220153_serialize_service_properties.rb b/db/migrate/20140907220153_serialize_service_properties.rb
index bd75ab1eacb606b0a6d420588cfc98ad42a88d50..d45a10465be7ad8ef62a537b8c2128d67c91eb7b 100644
--- a/db/migrate/20140907220153_serialize_service_properties.rb
+++ b/db/migrate/20140907220153_serialize_service_properties.rb
@@ -1,6 +1,9 @@
class SerializeServiceProperties < ActiveRecord::Migration
def change
- add_column :services, :properties, :text
+ unless column_exists?(:services, :properties)
+ add_column :services, :properties, :text
+ end
+
Service.reset_column_information
associations =
@@ -19,18 +22,21 @@ class SerializeServiceProperties < ActiveRecord::Migration
:api_version, :jira_issue_transition_id],
}
- Service.all.each do |service|
+ Service.find_each(batch_size: 500).each do |service|
associations[service.type.to_sym].each do |attribute|
service.send("#{attribute}=", service.attributes[attribute.to_s])
end
- service.save
+
+ service.save(validate: false)
end
- remove_column :services, :project_url, :string
- remove_column :services, :subdomain, :string
- remove_column :services, :room, :string
- remove_column :services, :recipients, :text
- remove_column :services, :api_key, :string
- remove_column :services, :token, :string
+ if column_exists?(:services, :project_url)
+ remove_column :services, :project_url, :string
+ remove_column :services, :subdomain, :string
+ remove_column :services, :room, :string
+ remove_column :services, :recipients, :text
+ remove_column :services, :api_key, :string
+ remove_column :services, :token, :string
+ end
end
end
diff --git a/db/migrate/20141006143943_move_slack_service_to_webhook.rb b/db/migrate/20141006143943_move_slack_service_to_webhook.rb
index a8e07033a5d2d1ecaeda211f2e859e7905a4c912..5836cd6b8dba3fdfa9b7b0c782e915864c06e1ca 100644
--- a/db/migrate/20141006143943_move_slack_service_to_webhook.rb
+++ b/db/migrate/20141006143943_move_slack_service_to_webhook.rb
@@ -10,7 +10,7 @@ class MoveSlackServiceToWebhook < ActiveRecord::Migration
slack_service.properties.delete('subdomain')
# Room is configured on the Slack side
slack_service.properties.delete('room')
- slack_service.save
+ slack_service.save(validate: false)
end
end
end
diff --git a/db/migrate/20141121133009_add_timestamps_to_members.rb b/db/migrate/20141121133009_add_timestamps_to_members.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ef6d4dedf32c95a69f85cc471def6bd66b392f36
--- /dev/null
+++ b/db/migrate/20141121133009_add_timestamps_to_members.rb
@@ -0,0 +1,15 @@
+# In 20140914145549_migrate_to_new_members_model.rb we forgot to set the
+# created_at and updated_at times for new records in the 'members' table. This
+# became a problem after commit c8e78d972a5a628870eefca0f2ccea0199c55bda which
+# was added in GitLab 7.5. With this migration we ensure that all rows in
+# 'members' have at least some created_at and updated_at timestamp.
+class AddTimestampsToMembers < ActiveRecord::Migration
+ def up
+ execute "UPDATE members SET created_at = NOW() WHERE created_at is NULL"
+ execute "UPDATE members SET updated_at = NOW() WHERE updated_at is NULL"
+ end
+
+ def down
+ # no change
+ end
+end
diff --git a/db/migrate/20141121161704_add_identity_table.rb b/db/migrate/20141121161704_add_identity_table.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a85b0426cec8b36e762a94767ff302876501296b
--- /dev/null
+++ b/db/migrate/20141121161704_add_identity_table.rb
@@ -0,0 +1,46 @@
+class AddIdentityTable < ActiveRecord::Migration
+ def up
+ create_table :identities do |t|
+ t.string :extern_uid
+ t.string :provider
+ t.references :user
+ end
+
+ add_index :identities, :user_id
+
+ execute <
+See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows the `class`, `id`, and `style` attributes.
+
## Horizontal Rule
```
@@ -483,6 +488,10 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
+
+This line is also a separate paragraph, and...
+This line is on its own line, because the previous line ends with two
+spaces.
```
Here's a line for us to start with.
@@ -492,6 +501,10 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also begins a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
+This line is also a separate paragraph, and...
+This line is on its own line, because the previous line ends with two
+spaces.
+
## Tables
Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them.
diff --git a/doc/operations/README.md b/doc/operations/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1456c6c8e2abae2faf7076078e8157a96b71765
--- /dev/null
+++ b/doc/operations/README.md
@@ -0,0 +1,4 @@
+# GitLab operations
+
+- [Sidekiq MemoryKiller](sidekiq_memory_killer.md)
+- [Cleaning up Redis sessions](cleaning_up_redis_sessions.md)
diff --git a/doc/operations/cleaning_up_redis_sessions.md b/doc/operations/cleaning_up_redis_sessions.md
new file mode 100644
index 0000000000000000000000000000000000000000..93521e976d51e05204c34addbedc8cb0e4c514ab
--- /dev/null
+++ b/doc/operations/cleaning_up_redis_sessions.md
@@ -0,0 +1,52 @@
+# Cleaning up stale Redis sessions
+
+Since version 6.2, GitLab stores web user sessions as key-value pairs in Redis.
+Prior to GitLab 7.3, user sessions did not automatically expire from Redis. If
+you have been running a large GitLab server (thousands of users) since before
+GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis
+database after you upgrade to GitLab 7.3. You can also perform a cleanup while
+still running GitLab 7.2 or older, but in that case new stale sessions will
+start building up again after you clean up.
+
+In GitLab versions prior to 7.3.0, the session keys in Redis are 16-byte
+hexadecimal values such as '976aa289e2189b17d7ef525a6702ace9'. Starting with
+GitLab 7.3.0, the keys are
+prefixed with 'session:gitlab:', so they would look like
+'session:gitlab:976aa289e2189b17d7ef525a6702ace9'. Below we describe how to
+remove the keys in the old format.
+
+First we define a shell function with the proper Redis connection details.
+
+```
+rcli() {
+ # This example works for Omnibus installations of GitLab 7.3 or newer. For an
+ # installation from source you will have to change the socket path and the
+ # path to redis-cli.
+ sudo /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket "$@"
+}
+
+# test the new shell function; the response should be PONG
+rcli ping
+```
+
+Now we do a search to see if there are any session keys in the old format for
+us to clean up.
+
+```
+# returns the number of old-format session keys in Redis
+rcli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l
+```
+
+If the number is larger than zero, you can proceed to expire the keys from
+Redis. If the number is zero there is nothing to clean up.
+
+```
+# Tell Redis to expire each matched key after 600 seconds.
+rcli keys '*' | grep '^[a-f0-9]\{32\}$' | awk '{ print "expire", $0, 600 }' | rcli
+# This will print '(integer) 1' for each key that gets expired.
+```
+
+Over the next 15 minutes (10 minutes expiry time plus 5 minutes Redis
+background save interval) your Redis database will be compacted. If you are
+still using GitLab 7.2, users who are not clicking around in GitLab during the
+10 minute expiry window will be signed out of GitLab.
diff --git a/doc/operations/sidekiq_memory_killer.md b/doc/operations/sidekiq_memory_killer.md
new file mode 100644
index 0000000000000000000000000000000000000000..867b01b0d5ae746e2e3e459296ace116a9795f95
--- /dev/null
+++ b/doc/operations/sidekiq_memory_killer.md
@@ -0,0 +1,38 @@
+# Sidekiq MemoryKiller
+
+The GitLab Rails application code suffers from memory leaks. For web requests
+this problem is made manageable using
+[unicorn-worker-killer](https://github.com/kzk/unicorn-worker-killer) which
+restarts Unicorn worker processes in between requests when needed. The Sidekiq
+MemoryKiller applies the same approach to the Sidekiq processes used by GitLab
+to process background jobs.
+
+Unlike unicorn-worker-killer, which is enabled by default for all GitLab
+installations since GitLab 6.4, the Sidekiq MemoryKiller is enabled by default
+_only_ for Omnibus packages. The reason for this is that the MemoryKiller
+relies on Runit to restart Sidekiq after a memory-induced shutdown and GitLab
+installations from source do not all use Runit or an equivalent.
+
+With the default settings, the MemoryKiller will cause a Sidekiq restart no
+more often than once every 15 minutes, with the restart causing about one
+minute of delay for incoming background jobs.
+
+## Configuring the MemoryKiller
+
+The MemoryKiller is controlled using environment variables.
+
+- `SIDEKIQ_MEMORY_KILLER_MAX_RSS`: if this variable is set, and its value is
+ greater than 0, then after each Sidekiq job, the MemoryKiller will check the
+ RSS of the Sidekiq process that executed the job. If the RSS of the Sidekiq
+ process (expressed in kilobytes) exceeds SIDEKIQ_MEMORY_KILLER_MAX_RSS, a
+ delayed shutdown is triggered. The default value for Omnibus packages is set
+ [in the omnibus-gitlab
+ repository](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb).
+- `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`: defaults 900 seconds (15 minutes). When
+ a shutdown is triggered, the Sidekiq process will keep working normally for
+ another 15 minutes.
+- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT`: defaults to 30 seconds. When the grace
+ time has expired, the MemoryKiller tells Sidekiq to stop accepting new jobs.
+ Existing jobs get 30 seconds to finish. After that, the MemoryKiller tells
+ Sidekiq to shut down, and an external supervision mechanism (e.g. Runit) must
+ restart Sidekiq.
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index d561868c8bb213e7db322a20cdbe26b6c63c9e44..8cfa7f9c876f35bd3c739dc9d518a2ad05ae42b1 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -8,7 +8,6 @@ If a user is a GitLab administrator they receive all permissions.
## Project
-
| Action | Guest | Reporter | Developer | Master | Owner |
|---------------------------------------|---------|------------|-------------|----------|--------|
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -19,6 +18,7 @@ If a user is a GitLab administrator they receive all permissions.
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
+| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ |
| Write a wiki | | | ✓ | ✓ | ✓ |
@@ -28,6 +28,7 @@ If a user is a GitLab administrator they receive all permissions.
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
| Enable/disable branch protection | | | | ✓ | ✓ |
+| Turn on/off prot. branch push for devs| | | | ✓ | ✓ |
| Rewrite/remove git tags | | | | ✓ | ✓ |
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
@@ -35,9 +36,16 @@ If a user is a GitLab administrator they receive all permissions.
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
+| Force push to protected branches | | | | | |
+| Remove protected branches | | | | | |
## Group
+In order for a group to appear as public and be browsable, it must contain at
+least one public project.
+
+Any user can remove themselves from a group, unless they are the last Owner of the group.
+
| Action | Guest | Reporter | Developer | Master | Owner |
|-------------------------|-------|----------|-----------|--------|-------|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -45,5 +53,3 @@ If a user is a GitLab administrator they receive all permissions.
| Create project in group | | | | ✓ | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
-
-Any user can remove himself from a group, unless he is the last Owner of the group.
diff --git a/doc/project_services/hipchat.md b/doc/project_services/hipchat.md
new file mode 100644
index 0000000000000000000000000000000000000000..021a93a288ff9de25dac5cda9cca1cef36a93795
--- /dev/null
+++ b/doc/project_services/hipchat.md
@@ -0,0 +1,54 @@
+# Atlassian HipChat
+
+GitLab provides a way to send HipChat notifications upon a number of events,
+such as when a user pushes code, creates a branch or tag, adds a comment, and
+creates a merge request.
+
+## Setup
+
+GitLab requires the use of a HipChat v2 API token to work. v1 tokens are
+not supported at this time. Note the differences between v1 and v2 tokens:
+
+HipChat v1 API (legacy) supports "API Auth Tokens" in the Group API menu. A v1
+token is allowed to send messages to *any* room.
+
+HipChat v2 API has tokens that are can be created using the Integrations tab
+in the Group or Room admin page. By design, these are lightweight tokens that
+allow GitLab to send messages only to *one* room.
+
+### Complete these steps in HipChat:
+
+1. Go to: https://admin.hipchat.com/admin
+1. Click on "Group Admin" -> "Integrations".
+1. Find "Build Your Own!" and click "Create".
+1. Select the desired room, name the integration "GitLab", and click "Create".
+1. In the "Send messages to this room by posting this URL" column, you should
+see a URL in the format:
+
+```
+ https://api.hipchat.com/v2/room/
it is @gfm\'s task.<\/code>/
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should handle references in " do
actual = "Apply _!#{merge_request.iid}_ ASAP"
- markdown(actual).should match(%r{Apply !#{merge_request.iid} })
+ expect(markdown(actual)).
+ to match(%r{Apply !#{merge_request.iid} })
end
it "should handle tables" do
@@ -560,91 +626,136 @@ describe GitlabMarkdownHelper do
| cell 1 | cell 2 |
| cell 3 | cell 4 |}
- markdown(actual).should match(/\A\n \n some code from $#{snippet.id}\nhere too\n
\n \n\n\n"
+ target_html = "some code from $#{snippet.id}\nhere too\n
\n"
- helper.markdown("\n some code from $#{snippet.id}\n here too\n").should == target_html
- helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should == target_html
+ expect(helper.markdown("\n some code from $#{snippet.id}\n here too\n")).
+ to eq(target_html)
+ expect(helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n")).
+ to eq(target_html)
end
it "should leave inline code untouched" do
- markdown("\nDon't use `$#{snippet.id}` here.\n").should ==
+ expect(markdown("\nDon't use `$#{snippet.id}` here.\n")).to eq(
"Don't use $#{snippet.id} here.
\n"
+ )
end
it "should leave ref-like autolinks untouched" do
- markdown("look at http://example.tld/#!#{merge_request.iid}").should == "look at http://example.tld/#!#{merge_request.iid}
\n"
+ expect(markdown("look at http://example.tld/#!#{merge_request.iid}")).to eq("look at http://example.tld/#!#{merge_request.iid}
\n")
end
it "should leave ref-like href of 'manual' links untouched" do
- markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})").should == "why not inspect !#{merge_request.iid}
\n"
+ expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("why not inspect !#{merge_request.iid}
\n")
end
it "should leave ref-like src of images untouched" do
- markdown("screen shot: ").should == "screen shot: 
\n"
+ expect(markdown("screen shot: ")).to eq("screen shot: 
\n")
end
it "should generate absolute urls for refs" do
- markdown("##{issue.iid}").should include(project_issue_url(project, issue))
+ expect(markdown("##{issue.iid}")).to include(namespace_project_issue_path(project.namespace, project, issue))
end
it "should generate absolute urls for emoji" do
- markdown(':smile:').should(
- include(%(src="#{Gitlab.config.gitlab.url}/assets/emoji/smile.png))
+ expect(markdown(':smile:')).to(
+ include(%(src="#{Gitlab.config.gitlab.url}/assets/emoji/#{Emoji.emoji_filename('smile')}.png))
)
end
it "should generate absolute urls for emoji if relative url is present" do
- Gitlab.config.gitlab.stub(:url).and_return('http://localhost/gitlab/root')
- markdown(":smile:").should include("src=\"http://localhost/gitlab/root/assets/emoji/smile.png")
+ allow(Gitlab.config.gitlab).to receive(:url).and_return('http://localhost/gitlab/root')
+ expect(markdown(":smile:")).to include("src=\"http://localhost/gitlab/root/assets/emoji/#{Emoji.emoji_filename('smile')}.png")
end
it "should generate absolute urls for emoji if asset_host is present" do
- Gitlab::Application.config.stub(:asset_host).and_return("https://cdn.example.com")
+ allow(Gitlab::Application.config).to receive(:asset_host).and_return("https://cdn.example.com")
ActionView::Base.any_instance.stub_chain(:config, :asset_host).and_return("https://cdn.example.com")
- markdown(":smile:").should include("src=\"https://cdn.example.com/assets/emoji/smile.png")
+ expect(markdown(":smile:")).to include("src=\"https://cdn.example.com/assets/emoji/#{Emoji.emoji_filename('smile')}.png")
end
it "should handle relative urls for a file in master" do
actual = "[GitLab API doc](doc/api/README.md)\n"
expected = "\n"
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
+ end
+
+ it "should handle relative urls for a file in master with an anchor" do
+ actual = "[GitLab API doc](doc/api/README.md#section)\n"
+ expected = "\n"
+ expect(markdown(actual)).to match(expected)
+ end
+
+ it "should not handle relative urls for the current file with an anchor" do
+ actual = "[GitLab API doc](#section)\n"
+ expected = "\n"
+ expect(markdown(actual)).to match(expected)
end
it "should handle relative urls for a directory in master" do
actual = "[GitLab API doc](doc/api)\n"
expected = "\n"
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should handle absolute urls" do
actual = "[GitLab](https://www.gitlab.com)\n"
expected = "\n"
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should handle relative urls in reference links for a file in master" do
actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n"
expected = "\n"
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should handle relative urls in reference links for a directory in master" do
actual = "[GitLab API doc directory][GitLab readmes]\n [GitLab readmes]: doc/api/\n"
expected = "\n"
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should not handle malformed relative urls in reference links for a file in master" do
actual = "[GitLab readme]: doc/api/README.md\n"
expected = ""
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
+ end
+
+ it 'should allow whitelisted HTML tags from the user' do
+ actual = '- Term
- Definition
'
+ expect(markdown(actual)).to match(actual)
+ end
+
+ it 'should sanitize tags that are not whitelisted' do
+ actual = ' '
+ expected = 'no inputs allowed no blinks'
+ expect(markdown(actual)).to match(expected)
+ expect(markdown(actual)).not_to match('<.textarea>')
+ expect(markdown(actual)).not_to match('<.blink>')
+ end
+
+ it 'should allow whitelisted tag attributes from the user' do
+ actual = 'link text'
+ expect(markdown(actual)).to match(actual)
+ end
+
+ it 'should sanitize tag attributes that are not whitelisted' do
+ actual = 'link text'
+ expected = 'link text'
+ expect(markdown(actual)).to match(expected)
+ end
+
+ it 'should sanitize javascript in attributes' do
+ actual = %q(link text)
+ expected = 'link text'
+ expect(markdown(actual)).to match(expected)
end
end
@@ -657,29 +768,29 @@ describe GitlabMarkdownHelper do
it "should not touch relative urls" do
actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n"
expected = "\n"
- markdown(actual).should match(expected)
+ expect(markdown(actual)).to match(expected)
end
end
describe "#render_wiki_content" do
before do
@wiki = double('WikiPage')
- @wiki.stub(:content).and_return('wiki content')
+ allow(@wiki).to receive(:content).and_return('wiki content')
end
it "should use GitLab Flavored Markdown for markdown files" do
- @wiki.stub(:format).and_return(:markdown)
+ allow(@wiki).to receive(:format).and_return(:markdown)
- helper.should_receive(:markdown).with('wiki content')
+ expect(helper).to receive(:markdown).with('wiki content')
helper.render_wiki_content(@wiki)
end
it "should use the Gollum renderer for all other file types" do
- @wiki.stub(:format).and_return(:rdoc)
+ allow(@wiki).to receive(:format).and_return(:rdoc)
formatted_content_stub = double('formatted_content')
- formatted_content_stub.should_receive(:html_safe)
- @wiki.stub(:formatted_content).and_return(formatted_content_stub)
+ expect(formatted_content_stub).to receive(:html_safe)
+ allow(@wiki).to receive(:formatted_content).and_return(formatted_content_stub)
helper.render_wiki_content(@wiki)
end
@@ -736,6 +847,17 @@ EOT
)
end
+ it 'should render checkboxes for nested tasks' do
+ rendered_text = markdown(@source_text_asterisk, parse_tasks: true)
+
+ expect(rendered_text).to match(
+ /Redmine\n" \
- ""
- end
-
- it "returns the correct issues trackers available with current tracker 'gitlab' selected" do
- project_issues_trackers('gitlab').should ==
- "\n" \
- ""
- end
-
- it "returns the correct issues trackers available with current tracker 'redmine' selected" do
- project_issues_trackers('redmine').should ==
- "\n" \
- ""
+ describe "#project_status_css_class" do
+ it "returns appropriate class" do
+ expect(project_status_css_class("started")).to eq("active")
+ expect(project_status_css_class("failed")).to eq("danger")
+ expect(project_status_css_class("finished")).to eq("success")
end
end
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 733f2754727329d401f795036c0bd3f2559b3840..b327f4f911a96d29dc78d6df6dae9ae4ee3c8313 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -13,7 +13,7 @@ describe SearchHelper do
end
it "it returns nil" do
- search_autocomplete_opts("q").should be_nil
+ expect(search_autocomplete_opts("q")).to be_nil
end
end
@@ -25,29 +25,29 @@ describe SearchHelper do
end
it "includes Help sections" do
- search_autocomplete_opts("hel").size.should == 9
+ expect(search_autocomplete_opts("hel").size).to eq(9)
end
it "includes default sections" do
- search_autocomplete_opts("adm").size.should == 1
+ expect(search_autocomplete_opts("adm").size).to eq(1)
end
it "includes the user's groups" do
create(:group).add_owner(user)
- search_autocomplete_opts("gro").size.should == 1
+ expect(search_autocomplete_opts("gro").size).to eq(1)
end
it "includes the user's projects" do
project = create(:project, namespace: create(:namespace, owner: user))
- search_autocomplete_opts(project.name).size.should == 1
+ expect(search_autocomplete_opts(project.name).size).to eq(1)
end
context "with a current project" do
before { @project = create(:project) }
it "includes project-specific sections" do
- search_autocomplete_opts("Files").size.should == 1
- search_autocomplete_opts("Commits").size.should == 1
+ expect(search_autocomplete_opts("Files").size).to eq(1)
+ expect(search_autocomplete_opts("Commits").size).to eq(1)
end
end
end
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index 41c9f038c266a5adeefa55cbdb75f77a675593a3..aef1108e3337843117948d52c36277d8115c260e 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -19,28 +19,28 @@ describe SubmoduleHelper do
Gitlab.config.gitlab_shell.stub(ssh_port: 22) # set this just to be sure
Gitlab.config.gitlab_shell.stub(ssh_path_prefix: Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url([ config.user, '@', config.host, ':gitlab-org/gitlab-ce.git' ].join(''))
- submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ]
+ expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should detect ssh on non-standard port' do
Gitlab.config.gitlab_shell.stub(ssh_port: 2222)
Gitlab.config.gitlab_shell.stub(ssh_path_prefix: Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url([ 'ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git' ].join(''))
- submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ]
+ expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should detect http on standard port' do
Gitlab.config.gitlab.stub(port: 80)
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
stub_url([ 'http://', config.host, '/gitlab-org/gitlab-ce.git' ].join(''))
- submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ]
+ expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should detect http on non-standard port' do
Gitlab.config.gitlab.stub(port: 3000)
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
stub_url([ 'http://', config.host, ':3000/gitlab-org/gitlab-ce.git' ].join(''))
- submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ]
+ expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should work with relative_url_root' do
@@ -48,67 +48,67 @@ describe SubmoduleHelper do
Gitlab.config.gitlab.stub(relative_url_root: '/gitlab/root')
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
stub_url([ 'http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git' ].join(''))
- submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ]
+ expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
end
context 'submodule on github.com' do
it 'should detect ssh' do
stub_url('git@github.com:gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]
+ expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ])
end
it 'should detect http' do
stub_url('http://github.com/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]
+ expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ])
end
it 'should detect https' do
stub_url('https://github.com/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]
+ expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ])
end
it 'should return original with non-standard url' do
stub_url('http://github.com/gitlab-org/gitlab-ce')
- submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ]
+ expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
stub_url('http://github.com/another/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ]
+ expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
end
end
context 'submodule on gitlab.com' do
it 'should detect ssh' do
stub_url('git@gitlab.com:gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]
+ expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ])
end
it 'should detect http' do
stub_url('http://gitlab.com/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]
+ expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ])
end
it 'should detect https' do
stub_url('https://gitlab.com/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]
+ expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ])
end
it 'should return original with non-standard url' do
stub_url('http://gitlab.com/gitlab-org/gitlab-ce')
- submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ]
+ expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
stub_url('http://gitlab.com/another/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ]
+ expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
end
end
context 'submodule on unsupported' do
it 'should return original' do
stub_url('http://mygitserver.com/gitlab-org/gitlab-ce')
- submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ]
+ expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
stub_url('http://mygitserver.com/gitlab-org/gitlab-ce.git')
- submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ]
+ expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ])
end
end
end
diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb
index fa8a3f554f75c892009d621a924eab1343415812..fc0ceecfbe7d8a10ca3b18710c80a29a3ba895bb 100644
--- a/spec/helpers/tab_helper_spec.rb
+++ b/spec/helpers/tab_helper_spec.rb
@@ -5,40 +5,40 @@ describe TabHelper do
describe 'nav_link' do
before do
- controller.stub(:controller_name).and_return('foo')
+ allow(controller).to receive(:controller_name).and_return('foo')
allow(self).to receive(:action_name).and_return('foo')
end
it "captures block output" do
- nav_link { "Testing Blocks" }.should match(/Testing Blocks/)
+ expect(nav_link { "Testing Blocks" }).to match(/Testing Blocks/)
end
it "performs checks on the current controller" do
- nav_link(controller: :foo).should match(//)
- nav_link(controller: :bar).should_not match(/active/)
- nav_link(controller: [:foo, :bar]).should match(/active/)
+ expect(nav_link(controller: :foo)).to match(/ /)
+ expect(nav_link(controller: :bar)).not_to match(/active/)
+ expect(nav_link(controller: [:foo, :bar])).to match(/active/)
end
it "performs checks on the current action" do
- nav_link(action: :foo).should match(/ /)
- nav_link(action: :bar).should_not match(/active/)
- nav_link(action: [:foo, :bar]).should match(/active/)
+ expect(nav_link(action: :foo)).to match(/ /)
+ expect(nav_link(action: :bar)).not_to match(/active/)
+ expect(nav_link(action: [:foo, :bar])).to match(/active/)
end
it "performs checks on both controller and action when both are present" do
- nav_link(controller: :bar, action: :foo).should_not match(/active/)
- nav_link(controller: :foo, action: :bar).should_not match(/active/)
- nav_link(controller: :foo, action: :foo).should match(/active/)
+ expect(nav_link(controller: :bar, action: :foo)).not_to match(/active/)
+ expect(nav_link(controller: :foo, action: :bar)).not_to match(/active/)
+ expect(nav_link(controller: :foo, action: :foo)).to match(/active/)
end
it "accepts a path shorthand" do
- nav_link(path: 'foo#bar').should_not match(/active/)
- nav_link(path: 'foo#foo').should match(/active/)
+ expect(nav_link(path: 'foo#bar')).not_to match(/active/)
+ expect(nav_link(path: 'foo#foo')).to match(/active/)
end
it "passes extra html options to the list element" do
- nav_link(action: :foo, html_options: {class: 'home'}).should match(/ /)
- nav_link(html_options: {class: 'active'}).should match(/ /)
+ expect(nav_link(action: :foo, html_options: {class: 'home'})).to match(/ /)
+ expect(nav_link(html_options: {class: 'active'})).to match(/ /)
end
end
end
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8271e00f41b24f03669224eaf86cc7d2653efa71
--- /dev/null
+++ b/spec/helpers/tree_helper_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe TreeHelper do
+ describe 'flatten_tree' do
+ let(:project) { create(:project) }
+
+ before {
+ @repository = project.repository
+ @commit = project.repository.commit("e56497bb")
+ }
+
+ context "on a directory containing more than one file/directory" do
+ let(:tree_item) { double(name: "files", path: "files") }
+
+ it "should return the directory name" do
+ expect(flatten_tree(tree_item)).to match('files')
+ end
+ end
+
+ context "on a directory containing only one directory" do
+ let(:tree_item) { double(name: "foo", path: "foo") }
+
+ it "should return the flattened path" do
+ expect(flatten_tree(tree_item)).to match('foo/bar')
+ end
+ end
+ end
+end
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb
index 8bf6ee2ed50851d2c37746442366da96d0f539e6..06d5450688baf556d40bec2390f8f93878e6690e 100644
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ b/spec/lib/disable_email_interceptor_spec.rb
@@ -6,7 +6,7 @@ describe DisableEmailInterceptor do
end
it 'should not send emails' do
- Gitlab.config.gitlab.stub(:email_enabled).and_return(false)
+ allow(Gitlab.config.gitlab).to receive(:email_enabled).and_return(false)
expect {
deliver_mail
}.not_to change(ActionMailer::Base.deliveries, :count)
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 7b3818ea5c8754962a922629b7844d1c0b06d576..ac602eac154bce6b5992dfe8a9149ff445fe2130 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -14,44 +14,46 @@ describe ExtractsPath do
describe '#extract_ref' do
it "returns an empty pair when no @project is set" do
@project = nil
- extract_ref('master/CHANGELOG').should == ['', '']
+ expect(extract_ref('master/CHANGELOG')).to eq(['', ''])
end
context "without a path" do
it "extracts a valid branch" do
- extract_ref('master').should == ['master', '']
+ expect(extract_ref('master')).to eq(['master', ''])
end
it "extracts a valid tag" do
- extract_ref('v2.0.0').should == ['v2.0.0', '']
+ expect(extract_ref('v2.0.0')).to eq(['v2.0.0', ''])
end
it "extracts a valid commit ref without a path" do
- extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062').should ==
+ expect(extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062')).to eq(
['f4b14494ef6abf3d144c28e4af0c20143383e062', '']
+ )
end
it "falls back to a primitive split for an invalid ref" do
- extract_ref('stable').should == ['stable', '']
+ expect(extract_ref('stable')).to eq(['stable', ''])
end
end
context "with a path" do
it "extracts a valid branch" do
- extract_ref('foo/bar/baz/CHANGELOG').should == ['foo/bar/baz', 'CHANGELOG']
+ expect(extract_ref('foo/bar/baz/CHANGELOG')).to eq(['foo/bar/baz', 'CHANGELOG'])
end
it "extracts a valid tag" do
- extract_ref('v2.0.0/CHANGELOG').should == ['v2.0.0', 'CHANGELOG']
+ expect(extract_ref('v2.0.0/CHANGELOG')).to eq(['v2.0.0', 'CHANGELOG'])
end
it "extracts a valid commit SHA" do
- extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG').should ==
+ expect(extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG')).to eq(
['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG']
+ )
end
it "falls back to a primitive split for an invalid ref" do
- extract_ref('stable/CHANGELOG').should == ['stable', 'CHANGELOG']
+ expect(extract_ref('stable/CHANGELOG')).to eq(['stable', 'CHANGELOG'])
end
end
end
diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/git_ref_validator_spec.rb
index b2469c18395edab10b10724374ade915e0c0d9cc..4633b6f3934b9ddf7e00c1cf3ea35e10d3da9f57 100644
--- a/spec/lib/git_ref_validator_spec.rb
+++ b/spec/lib/git_ref_validator_spec.rb
@@ -1,20 +1,20 @@
require 'spec_helper'
describe Gitlab::GitRefValidator do
- it { Gitlab::GitRefValidator.validate('feature/new').should be_true }
- it { Gitlab::GitRefValidator.validate('implement_@all').should be_true }
- it { Gitlab::GitRefValidator.validate('my_new_feature').should be_true }
- it { Gitlab::GitRefValidator.validate('#1').should be_true }
- it { Gitlab::GitRefValidator.validate('feature/~new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/^new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/:new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/?new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/*new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/[new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/new/').should be_false }
- it { Gitlab::GitRefValidator.validate('feature/new.').should be_false }
- it { Gitlab::GitRefValidator.validate('feature\@{').should be_false }
- it { Gitlab::GitRefValidator.validate('feature\new').should be_false }
- it { Gitlab::GitRefValidator.validate('feature//new').should be_false }
- it { Gitlab::GitRefValidator.validate('feature new').should be_false }
+ it { expect(Gitlab::GitRefValidator.validate('feature/new')).to be_truthy }
+ it { expect(Gitlab::GitRefValidator.validate('implement_@all')).to be_truthy }
+ it { expect(Gitlab::GitRefValidator.validate('my_new_feature')).to be_truthy }
+ it { expect(Gitlab::GitRefValidator.validate('#1')).to be_truthy }
+ it { expect(Gitlab::GitRefValidator.validate('feature/~new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/^new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/:new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/?new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/*new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/[new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/new/')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature/new.')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature\@{')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature\new')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature//new')).to be_falsey }
+ it { expect(Gitlab::GitRefValidator.validate('feature new')).to be_falsey }
end
diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d0aad54f677fa11e79a95c897a2689e91ae4de44
--- /dev/null
+++ b/spec/lib/gitlab/backend/grack_auth_spec.rb
@@ -0,0 +1,196 @@
+require "spec_helper"
+
+describe Grack::Auth do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ let(:app) { lambda { |env| [200, {}, "Success!"] } }
+ let!(:auth) { Grack::Auth.new(app) }
+ let(:env) {
+ {
+ "rack.input" => "",
+ "REQUEST_METHOD" => "GET",
+ "QUERY_STRING" => "service=git-upload-pack"
+ }
+ }
+ let(:status) { auth.call(env).first }
+
+ describe "#call" do
+ context "when the project doesn't exist" do
+ before do
+ env["PATH_INFO"] = "doesnt/exist.git"
+ end
+
+ context "when no authentication is provided" do
+ it "responds with status 401" do
+ expect(status).to eq(401)
+ end
+ end
+
+ context "when username and password are provided" do
+ context "when authentication fails" do
+ before do
+ env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
+ end
+
+ it "responds with status 401" do
+ expect(status).to eq(401)
+ end
+ end
+
+ context "when authentication succeeds" do
+ before do
+ env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+ end
+
+ it "responds with status 404" do
+ expect(status).to eq(404)
+ end
+ end
+ end
+ end
+
+ context "when the project exists" do
+ before do
+ env["PATH_INFO"] = project.path_with_namespace + ".git"
+ end
+
+ context "when the project is public" do
+ before do
+ project.update_attribute(:visibility_level, Project::PUBLIC)
+ end
+
+ it "responds with status 200" do
+ expect(status).to eq(200)
+ end
+ end
+
+ context "when the project is private" do
+ before do
+ project.update_attribute(:visibility_level, Project::PRIVATE)
+ end
+
+ context "when no authentication is provided" do
+ it "responds with status 401" do
+ expect(status).to eq(401)
+ end
+ end
+
+ context "when username and password are provided" do
+ context "when authentication fails" do
+ before do
+ env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
+ end
+
+ it "responds with status 401" do
+ expect(status).to eq(401)
+ end
+
+ context "when the user is IP banned" do
+ before do
+ expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true)
+ allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4')
+ end
+
+ it "responds with status 401" do
+ expect(status).to eq(401)
+ end
+ end
+ end
+
+ context "when authentication succeeds" do
+ before do
+ env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+ end
+
+ context "when the user has access to the project" do
+ before do
+ project.team << [user, :master]
+ end
+
+ context "when the user is blocked" do
+ before do
+ user.block
+ project.team << [user, :master]
+ end
+
+ it "responds with status 404" do
+ expect(status).to eq(404)
+ end
+ end
+
+ context "when the user isn't blocked" do
+ before do
+ expect(Rack::Attack::Allow2Ban).to receive(:reset)
+ end
+
+ it "responds with status 200" do
+ expect(status).to eq(200)
+ end
+ end
+
+ context "when blank password attempts follow a valid login" do
+ let(:options) { Gitlab.config.rack_attack.git_basic_auth }
+ let(:maxretry) { options[:maxretry] - 1 }
+ let(:ip) { '1.2.3.4' }
+
+ before do
+ allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip)
+ Rack::Attack::Allow2Ban.reset(ip, options)
+ end
+
+ after do
+ Rack::Attack::Allow2Ban.reset(ip, options)
+ end
+
+ def attempt_login(include_password)
+ password = include_password ? user.password : ""
+ env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, password)
+ Grack::Auth.new(app)
+ auth.call(env).first
+ end
+
+ it "repeated attempts followed by successful attempt" do
+ for n in 0..maxretry do
+ expect(attempt_login(false)).to eq(401)
+ end
+
+ expect(attempt_login(true)).to eq(200)
+ expect(Rack::Attack::Allow2Ban.send(:banned?, ip)).to eq(nil)
+
+ for n in 0..maxretry do
+ expect(attempt_login(false)).to eq(401)
+ end
+ end
+ end
+ end
+
+ context "when the user doesn't have access to the project" do
+ it "responds with status 404" do
+ expect(status).to eq(404)
+ end
+ end
+ end
+ end
+
+ context "when a gitlab ci token is provided" do
+ let(:token) { "123" }
+
+ before do
+ gitlab_ci_service = project.build_gitlab_ci_service
+ gitlab_ci_service.active = true
+ gitlab_ci_service.token = token
+ gitlab_ci_service.project_url = "http://google.com"
+ gitlab_ci_service.save
+
+ env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token)
+ end
+
+ it "responds with status 200" do
+ expect(status).to eq(200)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/backend/rack_attack_helpers_spec.rb b/spec/lib/gitlab/backend/rack_attack_helpers_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2ac496fd669a62cb3b48b0dd9377d580a6df8bbf
--- /dev/null
+++ b/spec/lib/gitlab/backend/rack_attack_helpers_spec.rb
@@ -0,0 +1,35 @@
+require "spec_helper"
+
+describe 'RackAttackHelpers' do
+ describe 'reset' do
+ let(:discriminator) { 'test-key'}
+ let(:maxretry) { 5 }
+ let(:period) { 1.minute }
+ let(:options) { { findtime: period, bantime: 60, maxretry: maxretry } }
+
+ def do_filter
+ for i in 1..maxretry - 1 do
+ status = Rack::Attack::Allow2Ban.filter(discriminator, options) { true }
+ expect(status).to eq(false)
+ end
+ end
+
+ def do_reset
+ Rack::Attack::Allow2Ban.reset(discriminator, options)
+ end
+
+ before do
+ do_reset
+ end
+
+ after do
+ do_reset
+ end
+
+ it 'user is not banned after n - 1 retries' do
+ do_filter
+ do_reset
+ do_filter
+ end
+ end
+end
diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb
index f00ec0fa401504350dc1a0e155aeabf19e6230c5..27279465c1aece0b6f00bccfac48a57520386e9c 100644
--- a/spec/lib/gitlab/backend/shell_spec.rb
+++ b/spec/lib/gitlab/backend/shell_spec.rb
@@ -8,11 +8,11 @@ describe Gitlab::Shell do
Project.stub(find: project)
end
- it { should respond_to :add_key }
- it { should respond_to :remove_key }
- it { should respond_to :add_repository }
- it { should respond_to :remove_repository }
- it { should respond_to :fork_repository }
+ it { is_expected.to respond_to :add_key }
+ it { is_expected.to respond_to :remove_key }
+ it { is_expected.to respond_to :add_repository }
+ it { is_expected.to respond_to :remove_repository }
+ it { is_expected.to respond_to :fork_repository }
- it { gitlab_shell.url_to_repo('diaspora').should == Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git" }
+ it { expect(gitlab_shell.url_to_repo('diaspora')).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git") }
end
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dd450e9967b338f0b0ffb2929fb1afd1749272c2
--- /dev/null
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Gitlab::BitbucketImport::Client do
+ let(:token) { '123456' }
+ let(:secret) { 'secret' }
+ let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) }
+
+ before do
+ Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
+ end
+
+ it 'all OAuth client options are symbols' do
+ client.consumer.options.keys.each do |key|
+ expect(key).to be_kind_of(Symbol)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f5523105848ced4551c48735d640ae8a49183ad1
--- /dev/null
+++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Gitlab::BitbucketImport::ProjectCreator do
+ let(:user) { create(:user, bitbucket_access_token: "asdffg", bitbucket_access_token_secret: "sekret") }
+ let(:repo) { {
+ name: 'Vim',
+ slug: 'vim',
+ is_private: true,
+ owner: "asd"}.with_indifferent_access
+ }
+ let(:namespace){ create(:namespace) }
+
+ it 'creates project' do
+ allow_any_instance_of(Project).to receive(:add_import_job)
+
+ project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, user)
+ project = project_creator.execute
+
+ expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git")
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c96ee78e5fdaea6801eed2e3c322d30045a7e054
--- /dev/null
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -0,0 +1,174 @@
+require 'spec_helper'
+
+describe Gitlab::ClosingIssueExtractor do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+ let(:iid1) { issue.iid }
+
+ describe :closed_by_message_in_project do
+ context 'with a single reference' do
+ it do
+ message = "Awesome commit (Closes ##{iid1})"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Awesome commit (closes ##{iid1})"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Closed ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "closed ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Closing ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "closing ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Close ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "close ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Awesome commit (Fixes ##{iid1})"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Awesome commit (fixes ##{iid1})"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Fixed ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "fixed ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Fixing ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "fixing ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Fix ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "fix ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Awesome commit (Resolves ##{iid1})"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Awesome commit (resolves ##{iid1})"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Resolved ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "resolved ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Resolving ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "resolving ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "Resolve ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+
+ it do
+ message = "resolve ##{iid1}"
+ expect(subject.closed_by_message_in_project(message, project)).to eq([issue])
+ end
+ end
+
+ context 'with multiple references' do
+ let(:other_issue) { create(:issue, project: project) }
+ let(:third_issue) { create(:issue, project: project) }
+ let(:iid2) { other_issue.iid }
+ let(:iid3) { third_issue.iid }
+
+ it 'fetches issues in single line message' do
+ message = "Closes ##{iid1} and fix ##{iid2}"
+
+ expect(subject.closed_by_message_in_project(message, project)).
+ to eq([issue, other_issue])
+ end
+
+ it 'fetches comma-separated issues references in single line message' do
+ message = "Closes ##{iid1}, closes ##{iid2}"
+
+ expect(subject.closed_by_message_in_project(message, project)).
+ to eq([issue, other_issue])
+ end
+
+ it 'fetches comma-separated issues numbers in single line message' do
+ message = "Closes ##{iid1}, ##{iid2} and ##{iid3}"
+
+ expect(subject.closed_by_message_in_project(message, project)).
+ to eq([issue, other_issue, third_issue])
+ end
+
+ it 'fetches issues in multi-line message' do
+ message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}"
+
+ expect(subject.closed_by_message_in_project(message, project)).
+ to eq([issue, other_issue])
+ end
+
+ it 'fetches issues in hybrid message' do
+ message = "Awesome commit (closes ##{iid1})\n"\
+ "Also fixing issues ##{iid2}, ##{iid3} and #4"
+
+ expect(subject.closed_by_message_in_project(message, project)).
+ to eq([issue, other_issue, third_issue])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index cf0b5c282c1d7a75195dbb921f7708d497e68180..40eb45e37cafd9348b73200e5893312c01889672 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -11,11 +11,11 @@ describe Gitlab::Diff::File do
describe :diff_lines do
let(:diff_lines) { diff_file.diff_lines }
- it { diff_lines.size.should == 30 }
- it { diff_lines.first.should be_kind_of(Gitlab::Diff::Line) }
+ it { expect(diff_lines.size).to eq(30) }
+ it { expect(diff_lines.first).to be_kind_of(Gitlab::Diff::Line) }
end
describe :mode_changed? do
- it { diff_file.mode_changed?.should be_false }
+ it { expect(diff_file.mode_changed?).to be_falsey }
end
end
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index 35b78260acd75eb6fddcc7f312ffb2e6b802bbba..918f6d0ead4f11b408d30f4fb06918558cdcdd30 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -50,43 +50,43 @@ eos
@lines = parser.parse(diff.lines)
end
- it { @lines.size.should == 30 }
+ it { expect(@lines.size).to eq(30) }
describe 'lines' do
describe 'first line' do
let(:line) { @lines.first }
- it { line.type.should == 'match' }
- it { line.old_pos.should == 6 }
- it { line.new_pos.should == 6 }
- it { line.text.should == '@@ -6,12 +6,18 @@ module Popen' }
+ it { expect(line.type).to eq('match') }
+ it { expect(line.old_pos).to eq(6) }
+ it { expect(line.new_pos).to eq(6) }
+ it { expect(line.text).to eq('@@ -6,12 +6,18 @@ module Popen') }
end
describe 'removal line' do
let(:line) { @lines[10] }
- it { line.type.should == 'old' }
- it { line.old_pos.should == 14 }
- it { line.new_pos.should == 13 }
- it { line.text.should == '- options = { chdir: path }' }
+ it { expect(line.type).to eq('old') }
+ it { expect(line.old_pos).to eq(14) }
+ it { expect(line.new_pos).to eq(13) }
+ it { expect(line.text).to eq('- options = { chdir: path }') }
end
describe 'addition line' do
let(:line) { @lines[16] }
- it { line.type.should == 'new' }
- it { line.old_pos.should == 15 }
- it { line.new_pos.should == 18 }
- it { line.text.should == '+ options = {' }
+ it { expect(line.type).to eq('new') }
+ it { expect(line.old_pos).to eq(15) }
+ it { expect(line.new_pos).to eq(18) }
+ it { expect(line.text).to eq('+ options = {') }
end
describe 'unchanged line' do
let(:line) { @lines.last }
- it { line.type.should == nil }
- it { line.old_pos.should == 24 }
- it { line.new_pos.should == 31 }
- it { line.text.should == ' @cmd_output << stderr.read' }
+ it { expect(line.type).to eq(nil) }
+ it { expect(line.old_pos).to eq(24) }
+ it { expect(line.new_pos).to eq(31) }
+ it { expect(line.text).to eq(' @cmd_output << stderr.read') }
end
end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index fe0a6bbdabb2d951025c024b0f2f763da67ca1f2..39be9d646442c761595f5ef28952d97315558515 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1,18 +1,81 @@
require 'spec_helper'
describe Gitlab::GitAccess do
- let(:access) { Gitlab::GitAccess.new }
+ let(:access) { Gitlab::GitAccess.new(actor, project) }
let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:actor) { user }
- describe 'download_allowed?' do
+ describe 'can_push_to_branch?' do
+ describe 'push to none protected branch' do
+ it "returns true if user is a master" do
+ project.team << [user, :master]
+ expect(access.can_push_to_branch?("random_branch")).to be_truthy
+ end
+
+ it "returns true if user is a developer" do
+ project.team << [user, :developer]
+ expect(access.can_push_to_branch?("random_branch")).to be_truthy
+ end
+
+ it "returns false if user is a reporter" do
+ project.team << [user, :reporter]
+ expect(access.can_push_to_branch?("random_branch")).to be_falsey
+ end
+ end
+
+ describe 'push to protected branch' do
+ before do
+ @branch = create :protected_branch, project: project
+ end
+
+ it "returns true if user is a master" do
+ project.team << [user, :master]
+ expect(access.can_push_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it "returns false if user is a developer" do
+ project.team << [user, :developer]
+ expect(access.can_push_to_branch?(@branch.name)).to be_falsey
+ end
+
+ it "returns false if user is a reporter" do
+ project.team << [user, :reporter]
+ expect(access.can_push_to_branch?(@branch.name)).to be_falsey
+ end
+ end
+
+ describe 'push to protected branch if allowed for developers' do
+ before do
+ @branch = create :protected_branch, project: project, developers_can_push: true
+ end
+
+ it "returns true if user is a master" do
+ project.team << [user, :master]
+ expect(access.can_push_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it "returns true if user is a developer" do
+ project.team << [user, :developer]
+ expect(access.can_push_to_branch?(@branch.name)).to be_truthy
+ end
+
+ it "returns false if user is a reporter" do
+ project.team << [user, :reporter]
+ expect(access.can_push_to_branch?(@branch.name)).to be_falsey
+ end
+ end
+
+ end
+
+ describe 'download_access_check' do
describe 'master permissions' do
before { project.team << [user, :master] }
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check }
- it { should be_true }
+ it { expect(subject.allowed?).to be_truthy }
end
end
@@ -20,9 +83,9 @@ describe Gitlab::GitAccess do
before { project.team << [user, :guest] }
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check }
- it { should be_false }
+ it { expect(subject.allowed?).to be_falsey }
end
end
@@ -33,22 +96,42 @@ describe Gitlab::GitAccess do
end
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check }
- it { should be_false }
+ it { expect(subject.allowed?).to be_falsey }
end
end
describe 'without acccess to project' do
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check }
+
+ it { expect(subject.allowed?).to be_falsey }
+ end
+ end
+
+ describe 'deploy key permissions' do
+ let(:key) { create(:deploy_key) }
+ let(:actor) { key }
+
+ context 'pull code' do
+ context 'allowed' do
+ before { key.projects << project }
+ subject { access.download_access_check }
+
+ it { expect(subject.allowed?).to be_truthy }
+ end
+
+ context 'denied' do
+ subject { access.download_access_check }
- it { should be_false }
+ it { expect(subject.allowed?).to be_falsey }
+ end
end
end
end
- describe 'push_allowed?' do
+ describe 'push_access_check' do
def protect_feature_branch
create(:protected_branch, name: 'feature', project: project)
end
@@ -110,6 +193,13 @@ describe Gitlab::GitAccess do
}
end
+ def self.updated_permissions_matrix
+ updated_permissions_matrix = permissions_matrix.dup
+ updated_permissions_matrix[:developer][:push_protected_branch] = true
+ updated_permissions_matrix[:developer][:push_all] = true
+ updated_permissions_matrix
+ end
+
permissions_matrix.keys.each do |role|
describe "#{role} access" do
before { protect_feature_branch }
@@ -117,9 +207,26 @@ describe Gitlab::GitAccess do
permissions_matrix[role].each do |action, allowed|
context action do
- subject { access.push_allowed?(user, project, changes[action]) }
+ subject { access.push_access_check(changes[action]) }
+
+ it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ end
+ end
+ end
+ end
+
+ context "with enabled developers push to protected branches " do
+ updated_permissions_matrix.keys.each do |role|
+ describe "#{role} access" do
+ before { create(:protected_branch, name: 'feature', developers_can_push: true, project: project) }
+ before { project.team << [user, role] }
+
+ updated_permissions_matrix[role].each do |action, allowed|
+ context action do
+ subject { access.push_access_check(changes[action]) }
- it { should allowed ? be_true : be_false }
+ it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ end
end
end
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index ed5785b31e6828d6514d96634fb9032638f4dd1a..4cb91094cb3bf0bda9ff4352a3a4237e9c90d089 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GitAccessWiki do
- let(:access) { Gitlab::GitAccessWiki.new }
+ let(:access) { Gitlab::GitAccessWiki.new(user, project) }
let(:project) { create(:project) }
let(:user) { create(:user) }
@@ -11,9 +11,9 @@ describe Gitlab::GitAccessWiki do
project.team << [user, :developer]
end
- subject { access.push_allowed?(user, project, changes) }
+ subject { access.push_access_check(changes) }
- it { should be_true }
+ it { expect(subject.allowed?).to be_truthy }
end
def changes
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..26618120316ace4d5aef3ddf6eab3f5b7d546a21
--- /dev/null
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::Client do
+ let(:token) { '123456' }
+ let(:client) { Gitlab::GithubImport::Client.new(token) }
+
+ before do
+ Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "github")
+ end
+
+ it 'all OAuth2 client options are symbols' do
+ client.client.options.keys.each do |key|
+ expect(key).to be_kind_of(Symbol)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8d594a112d4646378651e51af0c3d9691e248903
--- /dev/null
+++ b/spec/lib/gitlab/github_import/project_creator_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::ProjectCreator do
+ let(:user) { create(:user, github_access_token: "asdffg") }
+ let(:repo) { OpenStruct.new(
+ login: 'vim',
+ name: 'vim',
+ private: true,
+ full_name: 'asd/vim',
+ clone_url: "https://gitlab.com/asd/vim.git",
+ owner: OpenStruct.new(login: "john"))
+ }
+ let(:namespace){ create(:namespace) }
+
+ it 'creates project' do
+ allow_any_instance_of(Project).to receive(:add_import_job)
+
+ project_creator = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, user)
+ project = project_creator.execute
+
+ expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git")
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+end
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c511c51547409ac98519b97c4b4de59159c4d139
--- /dev/null
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe Gitlab::GitlabImport::Client do
+ let(:token) { '123456' }
+ let(:client) { Gitlab::GitlabImport::Client.new(token) }
+
+ before do
+ Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "gitlab")
+ end
+
+ it 'all OAuth2 client options are symbols' do
+ client.client.options.keys.each do |key|
+ expect(key).to be_kind_of(Symbol)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4c0d64ed1389930388a41b9d0384bddb2422dc65
--- /dev/null
+++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::GitlabImport::ProjectCreator do
+ let(:user) { create(:user, gitlab_access_token: "asdffg") }
+ let(:repo) { {
+ name: 'vim',
+ path: 'vim',
+ visibility_level: Gitlab::VisibilityLevel::PRIVATE,
+ path_with_namespace: 'asd/vim',
+ http_url_to_repo: "https://gitlab.com/asd/vim.git",
+ owner: {name: "john"}}.with_indifferent_access
+ }
+ let(:namespace){ create(:namespace) }
+
+ it 'creates project' do
+ allow_any_instance_of(Project).to receive(:add_import_job)
+
+ project_creator = Gitlab::GitlabImport::ProjectCreator.new(repo, namespace, user)
+ project = project_creator.execute
+
+ expect(project.import_url).to eq("https://oauth2:asdffg@gitlab.com/asd/vim.git")
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+end
diff --git a/spec/lib/gitlab/gitlab_markdown_helper_spec.rb b/spec/lib/gitlab/gitlab_markdown_helper_spec.rb
index 540618a560305b1ed8650a9915e7459a28475af8..ab613193f41de7f355312961204c7aafc1d8074f 100644
--- a/spec/lib/gitlab/gitlab_markdown_helper_spec.rb
+++ b/spec/lib/gitlab/gitlab_markdown_helper_spec.rb
@@ -5,24 +5,24 @@ describe Gitlab::MarkdownHelper do
%w(textile rdoc org creole wiki
mediawiki rst adoc asciidoc asc).each do |type|
it "returns true for #{type} files" do
- Gitlab::MarkdownHelper.markup?("README.#{type}").should be_true
+ expect(Gitlab::MarkdownHelper.markup?("README.#{type}")).to be_truthy
end
end
it 'returns false when given a non-markup filename' do
- Gitlab::MarkdownHelper.markup?('README.rb').should_not be_true
+ expect(Gitlab::MarkdownHelper.markup?('README.rb')).not_to be_truthy
end
end
describe '#gitlab_markdown?' do
%w(mdown md markdown).each do |type|
it "returns true for #{type} files" do
- Gitlab::MarkdownHelper.gitlab_markdown?("README.#{type}").should be_true
+ expect(Gitlab::MarkdownHelper.gitlab_markdown?("README.#{type}")).to be_truthy
end
end
it 'returns false when given a non-markdown filename' do
- Gitlab::MarkdownHelper.gitlab_markdown?('README.rb').should_not be_true
+ expect(Gitlab::MarkdownHelper.gitlab_markdown?('README.rb')).not_to be_truthy
end
end
end
diff --git a/spec/lib/gitlab/gitorious_import/project_creator.rb b/spec/lib/gitlab/gitorious_import/project_creator.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cf2318bb3a2e6764600e2679a18ad388de0ea686
--- /dev/null
+++ b/spec/lib/gitlab/gitorious_import/project_creator.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::GitoriousImport::ProjectCreator do
+ let(:user) { create(:user) }
+ let(:repo) { Gitlab::GitoriousImport::Repository.new('foo/bar-baz-qux') }
+ let(:namespace){ create(:namespace) }
+
+ it 'creates project' do
+ allow_any_instance_of(Project).to receive(:add_import_job)
+
+ project_creator = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, user)
+ project_creator.execute
+ project = Project.last
+
+ expect(project.name).to eq("Bar Baz Qux")
+ expect(project.path).to eq("bar-baz-qux")
+ expect(project.namespace).to eq(namespace)
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ expect(project.import_type).to eq("gitorious")
+ expect(project.import_source).to eq("foo/bar-baz-qux")
+ expect(project.import_url).to eq("https://gitorious.org/foo/bar-baz-qux.git")
+ end
+end
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index f4d5a9273968f736e2296c5edb0a58f13123b797..707a0521ab3a0a670c0ba5156ca7ad1d2f4b5aab 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::LDAP::Access do
let(:access) { Gitlab::LDAP::Access.new user }
- let(:user) { create(:user, :ldap) }
+ let(:user) { create(:omniauth_user) }
describe :allowed? do
subject { access.allowed? }
@@ -10,7 +10,7 @@ describe Gitlab::LDAP::Access do
context 'when the user cannot be found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: nil) }
- it { should be_false }
+ it { is_expected.to be_falsey }
end
context 'when the user is found' do
@@ -19,13 +19,27 @@ describe Gitlab::LDAP::Access do
context 'and the user is diabled via active directory' do
before { Gitlab::LDAP::Person.stub(disabled_via_active_directory?: true) }
- it { should be_false }
+ it { is_expected.to be_falsey }
+
+ it "should block user in GitLab" do
+ access.allowed?
+ user.should be_blocked
+ end
end
context 'and has no disabled flag in active diretory' do
- before { Gitlab::LDAP::Person.stub(disabled_via_active_directory?: false) }
+ before do
+ user.block
+
+ Gitlab::LDAP::Person.stub(disabled_via_active_directory?: false)
+ end
+
+ it { is_expected.to be_truthy }
- it { should be_true }
+ it "should unblock user in GitLab" do
+ access.allowed?
+ user.should_not be_blocked
+ end
end
context 'without ActiveDirectory enabled' do
@@ -34,8 +48,8 @@ describe Gitlab::LDAP::Access do
Gitlab::LDAP::Config.any_instance.stub(active_directory: false)
end
- it { should be_true }
+ it { is_expected.to be_truthy }
end
end
end
-end
\ No newline at end of file
+end
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index 19347e47378c60b88e55369b90f1c6b5f09e895f..b609e4b38f21c28c21445bded0723288922e1906 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -12,20 +12,20 @@ describe Gitlab::LDAP::Adapter do
context "and the result is non-empty" do
before { ldap.stub(search: [:foo]) }
- it { should be_true }
+ it { is_expected.to be_truthy }
end
context "and the result is empty" do
before { ldap.stub(search: []) }
- it { should be_false }
+ it { is_expected.to be_falsey }
end
end
context "when the search encounters an error" do
before { ldap.stub(search: nil, get_operation_result: double(code: 1, message: 'some error')) }
- it { should be_false }
+ it { is_expected.to be_falsey }
end
end
end
diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb
index 0eb7c443b8bb60ca9d6452f43216b0f53699e830..8afc2b21f467ac848fadfea567ff32f3a2f27c39 100644
--- a/spec/lib/gitlab/ldap/authentication_spec.rb
+++ b/spec/lib/gitlab/ldap/authentication_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::LDAP::Authentication do
let(:klass) { Gitlab::LDAP::Authentication }
- let(:user) { create(:user, :ldap, extern_uid: dn) }
+ let(:user) { create(:omniauth_user, extern_uid: dn) }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:login) { 'john' }
let(:password) { 'password' }
@@ -19,7 +19,7 @@ describe Gitlab::LDAP::Authentication do
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
- expect(klass.login(login, password)).to be_true
+ expect(klass.login(login, password)).to be_truthy
end
it "is false if the user does not exist" do
@@ -27,27 +27,27 @@ describe Gitlab::LDAP::Authentication do
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
- expect(klass.login(login, password)).to be_false
+ expect(klass.login(login, password)).to be_falsey
end
it "is false if authentication fails" do
user
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, bind_as: nil))
- expect(klass.login(login, password)).to be_false
+ expect(klass.login(login, password)).to be_falsey
end
it "fails if ldap is disabled" do
Gitlab::LDAP::Config.stub(enabled?: false)
- expect(klass.login(login, password)).to be_false
+ expect(klass.login(login, password)).to be_falsey
end
it "fails if no login is supplied" do
- expect(klass.login('', password)).to be_false
+ expect(klass.login('', password)).to be_falsey
end
it "fails if no password is supplied" do
- expect(klass.login(login, '')).to be_false
+ expect(klass.login(login, '')).to be_falsey
end
end
end
\ No newline at end of file
diff --git a/spec/lib/gitlab/ldap/config_spec.rb b/spec/lib/gitlab/ldap/config_spec.rb
index 3ebb8aae243a4e260dfd98db46c4d2408e91d30e..2df2beca7a601cd58cc17dd2da135cb3fe1a63e7 100644
--- a/spec/lib/gitlab/ldap/config_spec.rb
+++ b/spec/lib/gitlab/ldap/config_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::LDAP::Config do
let(:config) { Gitlab::LDAP::Config.new provider }
let(:provider) { 'ldapmain' }
- describe :initalize do
+ describe '#initalize' do
it 'requires a provider' do
expect{ Gitlab::LDAP::Config.new }.to raise_error ArgumentError
end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 726c9764e3d23ebf0faea6217fac03866e570531..4f93545feb699eb2d8903f045419653f0e9d7872 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -13,20 +13,37 @@ describe Gitlab::LDAP::User do
double(uid: 'my-uid', provider: 'ldapmain', info: double(info))
end
+ describe :changed? do
+ it "marks existing ldap user as changed" do
+ existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
+ expect(gl_user.changed?).to be_truthy
+ end
+
+ it "marks existing non-ldap user if the email matches as changed" do
+ existing_user = create(:user, email: 'john@example.com')
+ expect(gl_user.changed?).to be_truthy
+ end
+
+ it "dont marks existing ldap user as changed" do
+ existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
+ expect(gl_user.changed?).to be_falsey
+ end
+ end
+
describe :find_or_create do
it "finds the user if already existing" do
- existing_user = create(:user, extern_uid: 'my-uid', provider: 'ldapmain')
+ existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
expect{ gl_user.save }.to_not change{ User.count }
end
it "connects to existing non-ldap user if the email matches" do
- existing_user = create(:user, email: 'john@example.com')
+ existing_user = create(:omniauth_user, email: 'john@example.com', provider: "twitter")
expect{ gl_user.save }.to_not change{ User.count }
existing_user.reload
- expect(existing_user.extern_uid).to eql 'my-uid'
- expect(existing_user.provider).to eql 'ldapmain'
+ expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
+ expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
end
it "creates a new user if not found" do
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..448cd0c6880dcd58c23174cc62de6deec7453c2a
--- /dev/null
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe 'Gitlab::NoteDataBuilder' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:data) { Gitlab::NoteDataBuilder.build(note, user) }
+ let(:note_url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
+
+ before(:each) do
+ expect(data).to have_key(:object_attributes)
+ expect(data[:object_attributes]).to have_key(:url)
+ expect(data[:object_attributes][:url]).to eq(note_url)
+ expect(data[:object_kind]).to eq('note')
+ expect(data[:user]).to eq(user.hook_attrs)
+ end
+
+ describe 'When asking for a note on commit' do
+ let(:note) { create(:note_on_commit) }
+
+ it 'returns the note and commit-specific data' do
+ expect(data).to have_key(:commit)
+ end
+ end
+
+ describe 'When asking for a note on commit diff' do
+ let(:note) { create(:note_on_commit_diff) }
+
+ it 'returns the note and commit-specific data' do
+ expect(data).to have_key(:commit)
+ end
+ end
+
+ describe 'When asking for a note on issue' do
+ let(:issue) { create(:issue, created_at: fixed_time, updated_at: fixed_time) }
+ let(:note) { create(:note_on_issue, noteable_id: issue.id) }
+
+ it 'returns the note and issue-specific data' do
+ expect(data).to have_key(:issue)
+ expect(data[:issue]).to eq(issue.hook_attrs)
+ end
+ end
+
+ describe 'When asking for a note on merge request' do
+ let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
+ let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
+
+ it 'returns the note and merge request data' do
+ expect(data).to have_key(:merge_request)
+ expect(data[:merge_request]).to eq(merge_request.hook_attrs)
+ end
+ end
+
+ describe 'When asking for a note on merge request diff' do
+ let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
+ let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
+
+ it 'returns the note and merge request diff data' do
+ expect(data).to have_key(:merge_request)
+ expect(data[:merge_request]).to eq(merge_request.hook_attrs)
+ end
+ end
+
+ describe 'When asking for a note on project snippet' do
+ let!(:snippet) { create(:project_snippet, created_at: fixed_time, updated_at: fixed_time) }
+ let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
+
+ it 'returns the note and project snippet data' do
+ expect(data).to have_key(:snippet)
+ expect(data[:snippet]).to eq(snippet.hook_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/oauth/user_spec.rb b/spec/lib/gitlab/oauth/user_spec.rb
index 8a83a1b25880130091bccd3402a54978ba1a16d9..44cdd1e4fab191ee3d8f90166c3e6274b5131e9d 100644
--- a/spec/lib/gitlab/oauth/user_spec.rb
+++ b/spec/lib/gitlab/oauth/user_spec.rb
@@ -8,23 +8,23 @@ describe Gitlab::OAuth::User do
let(:auth_hash) { double(uid: uid, provider: provider, info: double(info_hash)) }
let(:info_hash) do
{
- nickname: 'john',
+ nickname: '-john+gitlab-ETC%.git@gmail.com',
name: 'John',
email: 'john@mail.com'
}
end
describe :persisted? do
- let!(:existing_user) { create(:user, extern_uid: 'my-uid', provider: 'my-provider') }
+ let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
it "finds an existing user based on uid and provider (facebook)" do
auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider')
- expect( oauth_user.persisted? ).to be_true
+ expect( oauth_user.persisted? ).to be_truthy
end
it "returns false if use is not found in database" do
auth_hash.stub(uid: 'non-existing')
- expect( oauth_user.persisted? ).to be_false
+ expect( oauth_user.persisted? ).to be_falsey
end
end
@@ -39,8 +39,9 @@ describe Gitlab::OAuth::User do
oauth_user.save
expect(gl_user).to be_valid
- expect(gl_user.extern_uid).to eql uid
- expect(gl_user.provider).to eql 'twitter'
+ identity = gl_user.identities.first
+ expect(identity.extern_uid).to eql uid
+ expect(identity.provider).to eql 'twitter'
end
end
@@ -61,8 +62,8 @@ describe Gitlab::OAuth::User do
it do
oauth_user.save
- gl_user.should be_valid
- gl_user.should_not be_blocked
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
end
end
@@ -71,8 +72,8 @@ describe Gitlab::OAuth::User do
it do
oauth_user.save
- gl_user.should be_valid
- gl_user.should be_blocked
+ expect(gl_user).to be_valid
+ expect(gl_user).to be_blocked
end
end
end
@@ -88,8 +89,8 @@ describe Gitlab::OAuth::User do
it do
oauth_user.save
- gl_user.should be_valid
- gl_user.should_not be_blocked
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
end
end
@@ -98,8 +99,8 @@ describe Gitlab::OAuth::User do
it do
oauth_user.save
- gl_user.should be_valid
- gl_user.should_not be_blocked
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
end
end
end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index 76d506eb3c0bf385fbba06d42e3f935f90e9bef1..cd9d0456b25c29b51ad8ceceed11dc67d00af87c 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -13,8 +13,8 @@ describe 'Gitlab::Popen', no_db: true do
@output, @status = @klass.new.popen(%W(ls), path)
end
- it { @status.should be_zero }
- it { @output.should include('cache') }
+ it { expect(@status).to be_zero }
+ it { expect(@output).to include('cache') }
end
context 'non-zero status' do
@@ -22,8 +22,8 @@ describe 'Gitlab::Popen', no_db: true do
@output, @status = @klass.new.popen(%W(cat NOTHING), path)
end
- it { @status.should == 1 }
- it { @output.should include('No such file or directory') }
+ it { expect(@status).to eq(1) }
+ it { expect(@output).to include('No such file or directory') }
end
context 'unsafe string command' do
@@ -37,8 +37,8 @@ describe 'Gitlab::Popen', no_db: true do
@output, @status = @klass.new.popen(%W(ls))
end
- it { @status.should be_zero }
- it { @output.should include('spec') }
+ it { expect(@status).to be_zero }
+ it { expect(@output).to include('spec') }
end
end
diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1b8ba7b4d4333d300f62845d8bfccaf1060ced1a
--- /dev/null
+++ b/spec/lib/gitlab/push_data_builder_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe 'Gitlab::PushDataBuilder' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+
+ describe :build_sample do
+ let(:data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ it { expect(data).to be_a(Hash) }
+ it { expect(data[:before]).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
+ it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+ it { expect(data[:ref]).to eq('refs/heads/master') }
+ it { expect(data[:commits].size).to eq(3) }
+ it { expect(data[:repository][:git_http_url]).to eq(project.http_url_to_repo) }
+ it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) }
+ it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) }
+ it { expect(data[:total_commits_count]).to eq(3) }
+ end
+
+ describe :build do
+ let(:data) do
+ Gitlab::PushDataBuilder.build(project,
+ user,
+ Gitlab::Git::BLANK_SHA,
+ '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b',
+ 'refs/tags/v1.1.0')
+ end
+
+ it { expect(data).to be_a(Hash) }
+ it { expect(data[:before]).to eq(Gitlab::Git::BLANK_SHA) }
+ it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+ it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') }
+ it { expect(data[:ref]).to eq('refs/tags/v1.1.0') }
+ it { expect(data[:commits]).to be_empty }
+ it { expect(data[:total_commits_count]).to be_zero }
+ end
+end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 23867df39dda8f7e7f32e452eecd830863308c1c..b3f4bb5aeda9137c09206e1409bd0f37da8dd8c8 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -3,52 +3,76 @@ require 'spec_helper'
describe Gitlab::ReferenceExtractor do
it 'extracts username references' do
subject.analyze('this contains a @user reference', nil)
- subject.users.should == [{ project: nil, id: 'user' }]
+ expect(subject.users).to eq([{ project: nil, id: 'user' }])
end
it 'extracts issue references' do
subject.analyze('this one talks about issue #1234', nil)
- subject.issues.should == [{ project: nil, id: '1234' }]
+ expect(subject.issues).to eq([{ project: nil, id: '1234' }])
end
it 'extracts JIRA issue references' do
- Gitlab.config.gitlab.stub(:issues_tracker).and_return('jira')
subject.analyze('this one talks about issue JIRA-1234', nil)
- subject.issues.should == [{ project: nil, id: 'JIRA-1234' }]
+ expect(subject.issues).to eq([{ project: nil, id: 'JIRA-1234' }])
end
it 'extracts merge request references' do
subject.analyze("and here's !43, a merge request", nil)
- subject.merge_requests.should == [{ project: nil, id: '43' }]
+ expect(subject.merge_requests).to eq([{ project: nil, id: '43' }])
end
it 'extracts snippet ids' do
subject.analyze('snippets like $12 get extracted as well', nil)
- subject.snippets.should == [{ project: nil, id: '12' }]
+ expect(subject.snippets).to eq([{ project: nil, id: '12' }])
end
it 'extracts commit shas' do
subject.analyze('commit shas 98cf0ae3 are pulled out as Strings', nil)
- subject.commits.should == [{ project: nil, id: '98cf0ae3' }]
+ expect(subject.commits).to eq([{ project: nil, id: '98cf0ae3' }])
+ end
+
+ it 'extracts commit ranges' do
+ subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4', nil)
+ expect(subject.commit_ranges).to eq([{ project: nil, id: '98cf0ae3...98cf0ae4' }])
end
it 'extracts multiple references and preserves their order' do
subject.analyze('@me and @you both care about this', nil)
- subject.users.should == [
+ expect(subject.users).to eq([
{ project: nil, id: 'me' },
{ project: nil, id: 'you' }
- ]
+ ])
end
it 'leaves the original note unmodified' do
text = 'issue #123 is just the worst, @user'
subject.analyze(text, nil)
- text.should == 'issue #123 is just the worst, @user'
+ expect(text).to eq('issue #123 is just the worst, @user')
+ end
+
+ it 'extracts no references for ..
blocks' do
+ subject.analyze("def puts '#1 issue'\nend\n
```", nil)
+ expect(subject.issues).to be_blank
+ end
+
+ it 'extracts no references for .. blocks' do
+ subject.analyze("def puts '!1 request'\nend\n```", nil)
+ expect(subject.merge_requests).to be_blank
+ end
+
+ it 'extracts no references for code blocks with language' do
+ subject.analyze("this code:\n```ruby\ndef puts '#1 issue'\nend\n```", nil)
+ expect(subject.issues).to be_blank
+ end
+
+ it 'extracts issue references for invalid code blocks' do
+ subject.analyze('test: ```this one talks about issue #1234```', nil)
+ expect(subject.issues).to eq([{ project: nil, id: '1234' }])
end
it 'handles all possible kinds of references' do
accessors = Gitlab::Markdown::TYPES.map { |t| "#{t}s".to_sym }
- subject.should respond_to(*accessors)
+ expect(subject).to respond_to(*accessors)
end
context 'with a project' do
@@ -63,7 +87,7 @@ describe Gitlab::ReferenceExtractor do
project.team << [@u_bar, :guest]
subject.analyze('@foo, @baduser, @bar, and @offteam', project)
- subject.users_for(project).should == [@u_foo, @u_bar]
+ expect(subject.users_for(project)).to eq([@u_foo, @u_bar])
end
it 'accesses valid issue objects' do
@@ -71,7 +95,7 @@ describe Gitlab::ReferenceExtractor do
@i1 = create(:issue, project: project)
subject.analyze("##{@i0.iid}, ##{@i1.iid}, and #999.", project)
- subject.issues_for(project).should == [@i0, @i1]
+ expect(subject.issues_for(project)).to eq([@i0, @i1])
end
it 'accesses valid merge requests' do
@@ -79,7 +103,7 @@ describe Gitlab::ReferenceExtractor do
@m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'bbb')
subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.", project)
- subject.merge_requests_for(project).should == [@m1, @m0]
+ expect(subject.merge_requests_for(project)).to eq([@m1, @m0])
end
it 'accesses valid snippets' do
@@ -88,7 +112,7 @@ describe Gitlab::ReferenceExtractor do
@s2 = create(:project_snippet)
subject.analyze("$#{@s0.id}, $999, $#{@s2.id}, $#{@s1.id}", project)
- subject.snippets_for(project).should == [@s0, @s1]
+ expect(subject.snippets_for(project)).to eq([@s0, @s1])
end
it 'accesses valid commits' do
@@ -97,9 +121,37 @@ describe Gitlab::ReferenceExtractor do
subject.analyze("this references commits #{commit.sha[0..6]} and 012345",
project)
extracted = subject.commits_for(project)
- extracted.should have(1).item
- extracted[0].sha.should == commit.sha
- extracted[0].message.should == commit.message
+ expect(extracted.size).to eq(1)
+ expect(extracted[0].sha).to eq(commit.sha)
+ expect(extracted[0].message).to eq(commit.message)
end
+
+ it 'accesses valid commit ranges' do
+ commit = project.repository.commit('master')
+ earlier_commit = project.repository.commit('master~2')
+
+ subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}",
+ project)
+ extracted = subject.commit_ranges_for(project)
+ expect(extracted.size).to eq(1)
+ expect(extracted[0][0].sha).to eq(earlier_commit.sha)
+ expect(extracted[0][0].message).to eq(earlier_commit.message)
+ expect(extracted[0][1].sha).to eq(commit.sha)
+ expect(extracted[0][1].message).to eq(commit.message)
+ end
+ end
+
+ context 'with a project with an underscore' do
+ let(:project) { create(:project, path: 'test_project') }
+ let(:issue) { create(:issue, project: project) }
+
+ it 'handles project issue references' do
+ subject.analyze("this refers issue #{project.path_with_namespace}##{issue.iid}",
+ project)
+ extracted = subject.issues_for(project)
+ expect(extracted.size).to eq(1)
+ expect(extracted).to eq([issue])
+ end
+
end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index a3aae7771bdef18065cb6e7c03b4a227273d022b..1db9f15b790f2ffd815b5202b46c7f0e06810f81 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -2,20 +2,20 @@ require 'spec_helper'
describe Gitlab::Regex do
describe 'path regex' do
- it { 'gitlab-ce'.should match(Gitlab::Regex.path_regex) }
- it { 'gitlab_git'.should match(Gitlab::Regex.path_regex) }
- it { '_underscore.js'.should match(Gitlab::Regex.path_regex) }
- it { '100px.com'.should match(Gitlab::Regex.path_regex) }
- it { '?gitlab'.should_not match(Gitlab::Regex.path_regex) }
- it { 'git lab'.should_not match(Gitlab::Regex.path_regex) }
- it { 'gitlab.git'.should_not match(Gitlab::Regex.path_regex) }
+ it { expect('gitlab-ce').to match(Gitlab::Regex.path_regex) }
+ it { expect('gitlab_git').to match(Gitlab::Regex.path_regex) }
+ it { expect('_underscore.js').to match(Gitlab::Regex.path_regex) }
+ it { expect('100px.com').to match(Gitlab::Regex.path_regex) }
+ it { expect('?gitlab').not_to match(Gitlab::Regex.path_regex) }
+ it { expect('git lab').not_to match(Gitlab::Regex.path_regex) }
+ it { expect('gitlab.git').not_to match(Gitlab::Regex.path_regex) }
end
describe 'project name regex' do
- it { 'gitlab-ce'.should match(Gitlab::Regex.project_name_regex) }
- it { 'GitLab CE'.should match(Gitlab::Regex.project_name_regex) }
- it { '100 lines'.should match(Gitlab::Regex.project_name_regex) }
- it { 'gitlab.git'.should match(Gitlab::Regex.project_name_regex) }
- it { '?gitlab'.should_not match(Gitlab::Regex.project_name_regex) }
+ it { expect('gitlab-ce').to match(Gitlab::Regex.project_name_regex) }
+ it { expect('GitLab CE').to match(Gitlab::Regex.project_name_regex) }
+ it { expect('100 lines').to match(Gitlab::Regex.project_name_regex) }
+ it { expect('gitlab.git').to match(Gitlab::Regex.project_name_regex) }
+ it { expect('?gitlab').not_to match(Gitlab::Regex.project_name_regex) }
end
end
diff --git a/spec/lib/gitlab/satellite/action_spec.rb b/spec/lib/gitlab/satellite/action_spec.rb
index 3eb1258d67ea61fd0872f2348c60cf51d3a27a4f..28e3d64ee2be0528484216f6dac785cd74de7339 100644
--- a/spec/lib/gitlab/satellite/action_spec.rb
+++ b/spec/lib/gitlab/satellite/action_spec.rb
@@ -6,7 +6,7 @@ describe 'Gitlab::Satellite::Action' do
describe '#prepare_satellite!' do
it 'should be able to fetch timeout from conf' do
- Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout].should == 30.seconds
+ expect(Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout]).to eq(30.seconds)
end
it 'create a repository with a parking branch and one remote: origin' do
@@ -15,22 +15,22 @@ describe 'Gitlab::Satellite::Action' do
#now lets dirty it up
starting_remote_count = repo.git.list_remotes.size
- starting_remote_count.should >= 1
+ expect(starting_remote_count).to be >= 1
#kind of hookey way to add a second remote
origin_uri = repo.git.remote({v: true}).split(" ")[1]
begin
repo.git.remote({raise: true}, 'add', 'another-remote', origin_uri)
repo.git.branch({raise: true}, 'a-new-branch')
- repo.heads.size.should > (starting_remote_count)
- repo.git.remote().split(" ").size.should > (starting_remote_count)
+ expect(repo.heads.size).to be > (starting_remote_count)
+ expect(repo.git.remote().split(" ").size).to be > (starting_remote_count)
rescue
end
repo.git.config({}, "user.name", "#{user.name} -- foo")
repo.git.config({}, "user.email", "#{user.email} -- foo")
- repo.config['user.name'].should =="#{user.name} -- foo"
- repo.config['user.email'].should =="#{user.email} -- foo"
+ expect(repo.config['user.name']).to eq("#{user.name} -- foo")
+ expect(repo.config['user.email']).to eq("#{user.email} -- foo")
#These must happen in the context of the satellite directory...
@@ -42,13 +42,13 @@ describe 'Gitlab::Satellite::Action' do
#verify it's clean
heads = repo.heads.map(&:name)
- heads.size.should == 1
- heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH).should == true
+ expect(heads.size).to eq(1)
+ expect(heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH)).to eq(true)
remotes = repo.git.remote().split(' ')
- remotes.size.should == 1
- remotes.include?('origin').should == true
- repo.config['user.name'].should ==user.name
- repo.config['user.email'].should ==user.email
+ expect(remotes.size).to eq(1)
+ expect(remotes.include?('origin')).to eq(true)
+ expect(repo.config['user.name']).to eq(user.name)
+ expect(repo.config['user.email']).to eq(user.email)
end
end
@@ -61,16 +61,16 @@ describe 'Gitlab::Satellite::Action' do
#set assumptions
FileUtils.rm_f(project.satellite.lock_file)
- File.exists?(project.satellite.lock_file).should be_false
+ expect(File.exists?(project.satellite.lock_file)).to be_falsey
satellite_action = Gitlab::Satellite::Action.new(user, project)
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
- repo.should == sat_repo
- (File.exists? project.satellite.lock_file).should be_true
+ expect(repo).to eq(sat_repo)
+ expect(File.exists? project.satellite.lock_file).to be_truthy
called = true
end
- called.should be_true
+ expect(called).to be_truthy
end
@@ -80,19 +80,19 @@ describe 'Gitlab::Satellite::Action' do
# Set base assumptions
if File.exists? project.satellite.lock_file
- FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false
+ expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey
end
satellite_action = Gitlab::Satellite::Action.new(user, project)
satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo|
called = true
- repo.should == sat_repo
- (File.exists? project.satellite.lock_file).should be_true
- FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_true
+ expect(repo).to eq(sat_repo)
+ expect(File.exists? project.satellite.lock_file).to be_truthy
+ expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_truthy
end
- called.should be_true
- FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false
+ expect(called).to be_truthy
+ expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey
end
diff --git a/spec/lib/gitlab/satellite/merge_action_spec.rb b/spec/lib/gitlab/satellite/merge_action_spec.rb
index 479a73a10817ed817efb7b7712f01dcdd02b3150..915e3ff0e5167219787780ca4b7f994fb1e2cb59 100644
--- a/spec/lib/gitlab/satellite/merge_action_spec.rb
+++ b/spec/lib/gitlab/satellite/merge_action_spec.rb
@@ -13,9 +13,9 @@ describe 'Gitlab::Satellite::MergeAction' do
describe '#commits_between' do
def verify_commits(commits, first_commit_sha, last_commit_sha)
- commits.each { |commit| commit.class.should == Gitlab::Git::Commit }
- commits.first.id.should == first_commit_sha
- commits.last.id.should == last_commit_sha
+ commits.each { |commit| expect(commit.class).to eq(Gitlab::Git::Commit) }
+ expect(commits.first.id).to eq(first_commit_sha)
+ expect(commits.last.id).to eq(last_commit_sha)
end
context 'on fork' do
@@ -35,7 +35,7 @@ describe 'Gitlab::Satellite::MergeAction' do
describe '#format_patch' do
def verify_content(patch)
sample_compare.commits.each do |commit|
- patch.include?(commit).should be_true
+ expect(patch.include?(commit)).to be_truthy
end
end
@@ -57,11 +57,11 @@ describe 'Gitlab::Satellite::MergeAction' do
describe '#diffs_between_satellite tested against diff_in_satellite' do
def is_a_matching_diff(diff, diffs)
diff_count = diff.scan('diff --git').size
- diff_count.should >= 1
- diffs.size.should == diff_count
+ expect(diff_count).to be >= 1
+ expect(diffs.size).to eq(diff_count)
diffs.each do |a_diff|
- a_diff.class.should == Gitlab::Git::Diff
- (diff.include? a_diff.diff).should be_true
+ expect(a_diff.class).to eq(Gitlab::Git::Diff)
+ expect(diff.include? a_diff.diff).to be_truthy
end
end
@@ -82,23 +82,23 @@ describe 'Gitlab::Satellite::MergeAction' do
describe '#can_be_merged?' do
context 'on fork' do
- it { Gitlab::Satellite::MergeAction.new(
+ it { expect(Gitlab::Satellite::MergeAction.new(
merge_request_fork.author,
- merge_request_fork).can_be_merged?.should be_true }
+ merge_request_fork).can_be_merged?).to be_truthy }
- it { Gitlab::Satellite::MergeAction.new(
+ it { expect(Gitlab::Satellite::MergeAction.new(
merge_request_fork_with_conflict.author,
- merge_request_fork_with_conflict).can_be_merged?.should be_false }
+ merge_request_fork_with_conflict).can_be_merged?).to be_falsey }
end
context 'between branches' do
- it { Gitlab::Satellite::MergeAction.new(
+ it { expect(Gitlab::Satellite::MergeAction.new(
merge_request.author,
- merge_request).can_be_merged?.should be_true }
+ merge_request).can_be_merged?).to be_truthy }
- it { Gitlab::Satellite::MergeAction.new(
+ it { expect(Gitlab::Satellite::MergeAction.new(
merge_request_with_conflict.author,
- merge_request_with_conflict).can_be_merged?.should be_false }
+ merge_request_with_conflict).can_be_merged?).to be_falsey }
end
end
end
diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb
index 2b254d6b3a6791ae748d629d13cc507b211ec432..ce3ea6c260aa66659caa758c328cb00f8a3a9fcf 100644
--- a/spec/lib/gitlab/upgrader_spec.rb
+++ b/spec/lib/gitlab/upgrader_spec.rb
@@ -5,20 +5,20 @@ describe Gitlab::Upgrader do
let(:current_version) { Gitlab::VERSION }
describe 'current_version_raw' do
- it { upgrader.current_version_raw.should == current_version }
+ it { expect(upgrader.current_version_raw).to eq(current_version) }
end
describe 'latest_version?' do
it 'should be true if newest version' do
upgrader.stub(latest_version_raw: current_version)
- upgrader.latest_version?.should be_true
+ expect(upgrader.latest_version?).to be_truthy
end
end
describe 'latest_version_raw' do
it 'should be latest version for GitLab 5' do
upgrader.stub(current_version_raw: "5.3.0")
- upgrader.latest_version_raw.should == "v5.4.2"
+ expect(upgrader.latest_version_raw).to eq("v5.4.2")
end
end
end
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index eb47bee8336b6141c84c4eb4a99e37c779fce225..5153ed15af3f865fed83d30545d9d99a82685639 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -5,7 +5,73 @@ describe Gitlab::UrlBuilder do
it 'returns the issue url' do
issue = create(:issue)
url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
- expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.to_param}/issues/#{issue.iid}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
+ end
+ end
+
+ describe 'When asking for an merge request' do
+ it 'returns the merge request url' do
+ merge_request = create(:merge_request)
+ url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
+ end
+ end
+
+ describe 'When asking for a note on commit' do
+ let(:note) { create(:note_on_commit) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on commit diff' do
+ let(:note) { create(:note_on_commit_diff) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on issue' do
+ let(:issue) { create(:issue) }
+ let(:note) { create(:note_on_issue, noteable_id: issue.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on merge request' do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on merge request diff' do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on project snippet' do
+ let(:snippet) { create(:project_snippet) }
+ let(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}"
end
end
end
diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb
index 94dccf7a4e5aeb59509855d1e2ea5f1921d3dad7..5afeb1c1ec3048de7691da7e2f8f0991200b7187 100644
--- a/spec/lib/gitlab/version_info_spec.rb
+++ b/spec/lib/gitlab/version_info_spec.rb
@@ -12,58 +12,58 @@ describe 'Gitlab::VersionInfo', no_db: true do
end
context '>' do
- it { @v2_0_0.should > @v1_1_0 }
- it { @v1_1_0.should > @v1_0_1 }
- it { @v1_0_1.should > @v1_0_0 }
- it { @v1_0_0.should > @v0_1_0 }
- it { @v0_1_0.should > @v0_0_1 }
+ it { expect(@v2_0_0).to be > @v1_1_0 }
+ it { expect(@v1_1_0).to be > @v1_0_1 }
+ it { expect(@v1_0_1).to be > @v1_0_0 }
+ it { expect(@v1_0_0).to be > @v0_1_0 }
+ it { expect(@v0_1_0).to be > @v0_0_1 }
end
context '>=' do
- it { @v2_0_0.should >= Gitlab::VersionInfo.new(2, 0, 0) }
- it { @v2_0_0.should >= @v1_1_0 }
+ it { expect(@v2_0_0).to be >= Gitlab::VersionInfo.new(2, 0, 0) }
+ it { expect(@v2_0_0).to be >= @v1_1_0 }
end
context '<' do
- it { @v0_0_1.should < @v0_1_0 }
- it { @v0_1_0.should < @v1_0_0 }
- it { @v1_0_0.should < @v1_0_1 }
- it { @v1_0_1.should < @v1_1_0 }
- it { @v1_1_0.should < @v2_0_0 }
+ it { expect(@v0_0_1).to be < @v0_1_0 }
+ it { expect(@v0_1_0).to be < @v1_0_0 }
+ it { expect(@v1_0_0).to be < @v1_0_1 }
+ it { expect(@v1_0_1).to be < @v1_1_0 }
+ it { expect(@v1_1_0).to be < @v2_0_0 }
end
context '<=' do
- it { @v0_0_1.should <= Gitlab::VersionInfo.new(0, 0, 1) }
- it { @v0_0_1.should <= @v0_1_0 }
+ it { expect(@v0_0_1).to be <= Gitlab::VersionInfo.new(0, 0, 1) }
+ it { expect(@v0_0_1).to be <= @v0_1_0 }
end
context '==' do
- it { @v0_0_1.should == Gitlab::VersionInfo.new(0, 0, 1) }
- it { @v0_1_0.should == Gitlab::VersionInfo.new(0, 1, 0) }
- it { @v1_0_0.should == Gitlab::VersionInfo.new(1, 0, 0) }
+ it { expect(@v0_0_1).to eq(Gitlab::VersionInfo.new(0, 0, 1)) }
+ it { expect(@v0_1_0).to eq(Gitlab::VersionInfo.new(0, 1, 0)) }
+ it { expect(@v1_0_0).to eq(Gitlab::VersionInfo.new(1, 0, 0)) }
end
context '!=' do
- it { @v0_0_1.should_not == @v0_1_0 }
+ it { expect(@v0_0_1).not_to eq(@v0_1_0) }
end
context 'unknown' do
- it { @unknown.should_not be @v0_0_1 }
- it { @unknown.should_not be Gitlab::VersionInfo.new }
+ it { expect(@unknown).not_to be @v0_0_1 }
+ it { expect(@unknown).not_to be Gitlab::VersionInfo.new }
it { expect{@unknown > @v0_0_1}.to raise_error(ArgumentError) }
it { expect{@unknown < @v0_0_1}.to raise_error(ArgumentError) }
end
context 'parse' do
- it { Gitlab::VersionInfo.parse("1.0.0").should == @v1_0_0 }
- it { Gitlab::VersionInfo.parse("1.0.0.1").should == @v1_0_0 }
- it { Gitlab::VersionInfo.parse("git 1.0.0b1").should == @v1_0_0 }
- it { Gitlab::VersionInfo.parse("git 1.0b1").should_not be_valid }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0.1")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("git 1.0.0b1")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("git 1.0b1")).not_to be_valid }
end
context 'to_s' do
- it { @v1_0_0.to_s.should == "1.0.0" }
- it { @unknown.to_s.should == "Unknown" }
+ it { expect(@v1_0_0.to_s).to eq("1.0.0") }
+ it { expect(@unknown.to_s).to eq("Unknown") }
end
end
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..af399f3a7319795710c5052736f1413f2c36baa7
--- /dev/null
+++ b/spec/lib/repository_cache_spec.rb
@@ -0,0 +1,34 @@
+require 'rspec'
+require_relative '../../lib/repository_cache'
+
+describe RepositoryCache do
+ let(:backend) { double('backend').as_null_object }
+ let(:cache) { RepositoryCache.new('example', backend) }
+
+ describe '#cache_key' do
+ it 'includes the namespace' do
+ expect(cache.cache_key(:foo)).to eq 'foo:example'
+ end
+ end
+
+ describe '#expire' do
+ it 'expires the given key from the cache' do
+ cache.expire(:foo)
+ expect(backend).to have_received(:delete).with('foo:example')
+ end
+ end
+
+ describe '#fetch' do
+ it 'fetches the given key from the cache' do
+ cache.fetch(:bar)
+ expect(backend).to have_received(:fetch).with('bar:example')
+ end
+
+ it 'accepts a block' do
+ p = -> {}
+
+ cache.fetch(:baz, &p)
+ expect(backend).to have_received(:fetch).with('baz:example', &p)
+ end
+ end
+end
diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb
index a3c353d5eab64c2fb8e2190adff9382bee5d78db..df243a26008dca1ab7fafe6907b95d21fc49717a 100644
--- a/spec/lib/votes_spec.rb
+++ b/spec/lib/votes_spec.rb
@@ -5,132 +5,181 @@ describe Issue, 'Votes' do
describe "#upvotes" do
it "with no notes has a 0/0 score" do
- issue.upvotes.should == 0
+ expect(issue.upvotes).to eq(0)
end
it "should recognize non-+1 notes" do
add_note "No +1 here"
- issue.should have(1).note
- issue.notes.first.upvote?.should be_false
- issue.upvotes.should == 0
+ expect(issue.notes.size).to eq(1)
+ expect(issue.notes.first.upvote?).to be_falsey
+ expect(issue.upvotes).to eq(0)
end
it "should recognize a single +1 note" do
add_note "+1 This is awesome"
- issue.upvotes.should == 1
+ expect(issue.upvotes).to eq(1)
end
- it "should recognize multiple +1 notes" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
- issue.upvotes.should == 2
+ it 'should recognize multiple +1 notes' do
+ add_note '+1 This is awesome', create(:user)
+ add_note '+1 I want this', create(:user)
+ expect(issue.upvotes).to eq(2)
+ end
+
+ it 'should not count 2 +1 votes from the same user' do
+ add_note '+1 This is awesome'
+ add_note '+1 I want this'
+ expect(issue.upvotes).to eq(1)
end
end
describe "#downvotes" do
it "with no notes has a 0/0 score" do
- issue.downvotes.should == 0
+ expect(issue.downvotes).to eq(0)
end
it "should recognize non--1 notes" do
add_note "Almost got a -1"
- issue.should have(1).note
- issue.notes.first.downvote?.should be_false
- issue.downvotes.should == 0
+ expect(issue.notes.size).to eq(1)
+ expect(issue.notes.first.downvote?).to be_falsey
+ expect(issue.downvotes).to eq(0)
end
it "should recognize a single -1 note" do
add_note "-1 This is bad"
- issue.downvotes.should == 1
+ expect(issue.downvotes).to eq(1)
end
it "should recognize multiple -1 notes" do
- add_note "-1 This is bad"
- add_note "-1 Away with this"
- issue.downvotes.should == 2
+ add_note('-1 This is bad', create(:user))
+ add_note('-1 Away with this', create(:user))
+ expect(issue.downvotes).to eq(2)
end
end
describe "#votes_count" do
it "with no notes has a 0/0 score" do
- issue.votes_count.should == 0
+ expect(issue.votes_count).to eq(0)
end
it "should recognize non notes" do
add_note "No +1 here"
- issue.should have(1).note
- issue.votes_count.should == 0
+ expect(issue.notes.size).to eq(1)
+ expect(issue.votes_count).to eq(0)
end
it "should recognize a single +1 note" do
add_note "+1 This is awesome"
- issue.votes_count.should == 1
+ expect(issue.votes_count).to eq(1)
end
it "should recognize a single -1 note" do
add_note "-1 This is bad"
- issue.votes_count.should == 1
+ expect(issue.votes_count).to eq(1)
end
it "should recognize multiple notes" do
- add_note "+1 This is awesome"
- add_note "-1 This is bad"
- add_note "+1 I want this"
- issue.votes_count.should == 3
+ add_note('+1 This is awesome', create(:user))
+ add_note('-1 This is bad', create(:user))
+ add_note('+1 I want this', create(:user))
+ expect(issue.votes_count).to eq(3)
+ end
+
+ it 'should not count 2 -1 votes from the same user' do
+ add_note '-1 This is suspicious'
+ add_note '-1 This is bad'
+ expect(issue.votes_count).to eq(1)
end
end
describe "#upvotes_in_percent" do
it "with no notes has a 0% score" do
- issue.upvotes_in_percent.should == 0
+ expect(issue.upvotes_in_percent).to eq(0)
end
it "should count a single 1 note as 100%" do
add_note "+1 This is awesome"
- issue.upvotes_in_percent.should == 100
+ expect(issue.upvotes_in_percent).to eq(100)
end
- it "should count multiple +1 notes as 100%" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
- issue.upvotes_in_percent.should == 100
+ it 'should count multiple +1 notes as 100%' do
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 I want this', create(:user))
+ expect(issue.upvotes_in_percent).to eq(100)
end
- it "should count fractions for multiple +1 and -1 notes correctly" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
- add_note "-1 This is bad"
- add_note "+1 me too"
- issue.upvotes_in_percent.should == 75
+ it 'should count fractions for multiple +1 and -1 notes correctly' do
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 I want this', create(:user))
+ add_note('-1 This is bad', create(:user))
+ add_note('+1 me too', create(:user))
+ expect(issue.upvotes_in_percent).to eq(75)
end
end
describe "#downvotes_in_percent" do
it "with no notes has a 0% score" do
- issue.downvotes_in_percent.should == 0
+ expect(issue.downvotes_in_percent).to eq(0)
end
it "should count a single -1 note as 100%" do
add_note "-1 This is bad"
- issue.downvotes_in_percent.should == 100
+ expect(issue.downvotes_in_percent).to eq(100)
end
- it "should count multiple -1 notes as 100%" do
- add_note "-1 This is bad"
- add_note "-1 Away with this"
- issue.downvotes_in_percent.should == 100
+ it 'should count multiple -1 notes as 100%' do
+ add_note('-1 This is bad', create(:user))
+ add_note('-1 Away with this', create(:user))
+ expect(issue.downvotes_in_percent).to eq(100)
end
- it "should count fractions for multiple +1 and -1 notes correctly" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
- add_note "-1 This is bad"
- add_note "+1 me too"
- issue.downvotes_in_percent.should == 25
+ it 'should count fractions for multiple +1 and -1 notes correctly' do
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 I want this', create(:user))
+ add_note('-1 This is bad', create(:user))
+ add_note('+1 me too', create(:user))
+ expect(issue.downvotes_in_percent).to eq(25)
+ end
+ end
+
+ describe '#filter_superceded_votes' do
+
+ it 'should count a users vote only once amongst multiple votes' do
+ add_note('-1 This needs work before I will accept it')
+ add_note('+1 I want this', create(:user))
+ add_note('+1 This is is awesome', create(:user))
+ add_note('+1 this looks good now')
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 me too', create(:user))
+ expect(issue.downvotes).to eq(0)
+ expect(issue.upvotes).to eq(5)
end
+
+ it 'should count each users vote only once' do
+ add_note '-1 This needs work before it will be accepted'
+ add_note '+1 I like this'
+ add_note '+1 I still like this'
+ add_note '+1 I really like this'
+ add_note '+1 Give me this now!!!!'
+ expect(issue.downvotes).to eq(0)
+ expect(issue.upvotes).to eq(1)
+ end
+
+ it 'should count a users vote only once without caring about comments' do
+ add_note '-1 This needs work before it will be accepted'
+ add_note 'Comment 1'
+ add_note 'Another comment'
+ add_note '+1 vote'
+ add_note 'final comment'
+ expect(issue.downvotes).to eq(0)
+ expect(issue.upvotes).to eq(1)
+ end
+
end
- def add_note(text)
- issue.notes << create(:note, note: text, project: issue.project)
+ def add_note(text, author = issue.author)
+ created_at = Time.now - 1.hour + Note.count.seconds
+ issue.notes << create(:note, note: text, project: issue.project,
+ author_id: author.id, created_at: created_at)
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index e06e8826e5c437544a78fe2aa86f257db4ba8c14..ba42f9e5c706589e144dac1f0f1b69dc96665ed0 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -5,38 +5,46 @@ describe Notify do
include EmailSpec::Matchers
include RepoHelpers
+ let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name }
let(:gitlab_sender) { Gitlab.config.gitlab.email_from }
let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) }
+ before(:each) do
+ email = recipient.emails.create(email: "notifications@example.com")
+ recipient.update_attribute(:notification_email, email.email)
+ end
+
shared_examples 'a multiple recipients email' do
it 'is sent to the given recipient' do
- should deliver_to recipient.email
+ is_expected.to deliver_to recipient.notification_email
end
end
shared_examples 'an email sent from GitLab' do
it 'is sent from GitLab' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq('GitLab')
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(gitlab_sender_display_name)
+ expect(sender.address).to eq(gitlab_sender)
end
end
shared_examples 'an email starting a new thread' do |message_id_prefix|
it 'has a discussion identifier' do
- should have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
end
end
shared_examples 'an answer to an existing thread' do |thread_id_prefix|
it 'has a subject that begins with Re: ' do
- should have_subject /^Re: /
+ is_expected.to have_subject /^Re: /
end
it 'has headers that reference an existing thread' do
- should have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
- should have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
end
end
@@ -46,35 +54,35 @@ describe Notify do
token = 'kETLwRaayvigPq_x3SNM'
- subject { Notify.new_user_email(new_user.id, new_user.password, token) }
+ subject { Notify.new_user_email(new_user.id, token) }
it_behaves_like 'an email sent from GitLab'
it 'is sent to the new user' do
- should deliver_to new_user.email
+ is_expected.to deliver_to new_user.email
end
it 'has the correct subject' do
- should have_subject /^Account was created for you$/i
+ is_expected.to have_subject /^Account was created for you$/i
end
it 'contains the new user\'s login name' do
- should have_body_text /#{new_user.email}/
+ is_expected.to have_body_text /#{new_user.email}/
end
it 'contains the password text' do
- should have_body_text /Click here to set your password/
+ is_expected.to have_body_text /Click here to set your password/
end
it 'includes a link for user to set password' do
params = "reset_password_token=#{token}"
- should have_body_text(
+ is_expected.to have_body_text(
%r{http://localhost(:\d+)?/users/password/edit\?#{params}}
)
end
it 'includes a link to the site' do
- should have_body_text /#{example_site_path}/
+ is_expected.to have_body_text /#{example_site_path}/
end
end
@@ -83,28 +91,28 @@ describe Notify do
let(:example_site_path) { root_path }
let(:new_user) { create(:user, email: 'newguy@example.com', password: "securePassword") }
- subject { Notify.new_user_email(new_user.id, new_user.password) }
+ subject { Notify.new_user_email(new_user.id) }
it_behaves_like 'an email sent from GitLab'
it 'is sent to the new user' do
- should deliver_to new_user.email
+ is_expected.to deliver_to new_user.email
end
it 'has the correct subject' do
- should have_subject /^Account was created for you$/i
+ is_expected.to have_subject /^Account was created for you$/i
end
it 'contains the new user\'s login name' do
- should have_body_text /#{new_user.email}/
+ is_expected.to have_body_text /#{new_user.email}/
end
it 'should not contain the new user\'s password' do
- should_not have_body_text /password/
+ is_expected.not_to have_body_text /password/
end
it 'includes a link to the site' do
- should have_body_text /#{example_site_path}/
+ is_expected.to have_body_text /#{example_site_path}/
end
end
@@ -116,19 +124,19 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it 'is sent to the new user' do
- should deliver_to key.user.email
+ is_expected.to deliver_to key.user.email
end
it 'has the correct subject' do
- should have_subject /^SSH key was added to your account$/i
+ is_expected.to have_subject /^SSH key was added to your account$/i
end
it 'contains the new ssh key title' do
- should have_body_text /#{key.title}/
+ is_expected.to have_body_text /#{key.title}/
end
it 'includes a link to ssh keys page' do
- should have_body_text /#{profile_keys_path}/
+ is_expected.to have_body_text /#{profile_keys_path}/
end
end
@@ -138,19 +146,19 @@ describe Notify do
subject { Notify.new_email_email(email.id) }
it 'is sent to the new user' do
- should deliver_to email.user.email
+ is_expected.to deliver_to email.user.email
end
it 'has the correct subject' do
- should have_subject /^Email was added to your account$/i
+ is_expected.to have_subject /^Email was added to your account$/i
end
it 'contains the new email address' do
- should have_body_text /#{email.email}/
+ is_expected.to have_body_text /#{email.email}/
end
it 'includes a link to emails page' do
- should have_body_text /#{profile_emails_path}/
+ is_expected.to have_body_text /#{profile_emails_path}/
end
end
@@ -163,18 +171,25 @@ describe Notify do
shared_examples 'an assignee email' do
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(current_user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'is sent to the assignee' do
- should deliver_to assignee.email
+ is_expected.to deliver_to assignee.email
end
end
context 'for issues' do
let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) }
let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: Faker::Lorem.sentence) }
+ let(:issue_with_image) do
+ create(:issue,
+ author: current_user,
+ assignee: assignee,
+ project: project,
+ description: "")
+ end
describe 'that are new' do
subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
@@ -183,11 +198,11 @@ describe Notify do
it_behaves_like 'an email starting a new thread', 'issue'
it 'has the correct subject' do
- should have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/
+ is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/
end
it 'contains a link to the new issue' do
- should have_body_text /#{project_issue_path project, issue}/
+ is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
end
@@ -195,7 +210,23 @@ describe Notify do
subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) }
it 'contains the description' do
- should have_body_text /#{issue_with_description.description}/
+ is_expected.to have_body_text /#{issue_with_description.description}/
+ end
+ end
+
+ describe 'that contain images' do
+ let(:png) { File.read("#{Rails.root}/spec/fixtures/dk.png") }
+ let(:png_encoded) { Base64::encode64(png) }
+
+ before :each do
+ file_path = File.join(Rails.root, 'public', 'uploads', issue_with_image.project.path_with_namespace, '12345/test.jpg')
+ allow(File).to receive(:file?).with(file_path).and_return(true)
+ allow(File).to receive(:read).with(file_path).and_return(png)
+ end
+
+ subject { Notify.new_issue_email(issue_with_image.assignee_id, issue_with_image.id) }
+ it 'replaces attached images with inline images' do
+ is_expected.to have_body_text URI.encode(png_encoded)
end
end
@@ -207,24 +238,24 @@ describe Notify do
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(current_user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'has the correct subject' do
- should have_subject /#{issue.title} \(##{issue.iid}\)/
+ is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
end
it 'contains the name of the previous assignee' do
- should have_body_text /#{previous_assignee.name}/
+ is_expected.to have_body_text /#{previous_assignee.name}/
end
it 'contains the name of the new assignee' do
- should have_body_text /#{assignee.name}/
+ is_expected.to have_body_text /#{assignee.name}/
end
it 'contains a link to the issue' do
- should have_body_text /#{project_issue_path project, issue}/
+ is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
end
@@ -236,24 +267,24 @@ describe Notify do
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(current_user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'has the correct subject' do
- should have_subject /#{issue.title} \(##{issue.iid}\)/i
+ is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i
end
it 'contains the new status' do
- should have_body_text /#{status}/i
+ is_expected.to have_body_text /#{status}/i
end
it 'contains the user name' do
- should have_body_text /#{current_user.name}/i
+ is_expected.to have_body_text /#{current_user.name}/i
end
it 'contains a link to the issue' do
- should have_body_text /#{project_issue_path project, issue}/
+ is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
end
@@ -263,6 +294,14 @@ describe Notify do
let(:merge_author) { create(:user) }
let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) }
+ let(:merge_request_with_image) do
+ create(:merge_request,
+ author: current_user,
+ assignee: assignee,
+ source_project: project,
+ target_project: project,
+ description: "")
+ end
describe 'that are new' do
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
@@ -271,23 +310,23 @@ describe Notify do
it_behaves_like 'an email starting a new thread', 'merge_request'
it 'has the correct subject' do
- should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end
it 'contains a link to the new merge request' do
- should have_body_text /#{project_merge_request_path(project, merge_request)}/
+ is_expected.to have_body_text /#{namespace_project_merge_request_path(project.namespace, project, merge_request)}/
end
it 'contains the source branch for the merge request' do
- should have_body_text /#{merge_request.source_branch}/
+ is_expected.to have_body_text /#{merge_request.source_branch}/
end
it 'contains the target branch for the merge request' do
- should have_body_text /#{merge_request.target_branch}/
+ is_expected.to have_body_text /#{merge_request.target_branch}/
end
it 'has the correct message-id set' do
- should have_header 'Message-ID', ""
+ is_expected.to have_header 'Message-ID', ""
end
end
@@ -295,7 +334,23 @@ describe Notify do
subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) }
it 'contains the description' do
- should have_body_text /#{merge_request_with_description.description}/
+ is_expected.to have_body_text /#{merge_request_with_description.description}/
+ end
+ end
+
+ describe 'that are new and contain contain images in the description' do
+ let(:png) {File.read("#{Rails.root}/spec/fixtures/dk.png")}
+ let(:png_encoded) { Base64::encode64(png) }
+
+ before :each do
+ file_path = File.join(Rails.root, 'public', 'uploads', merge_request_with_image.project.path_with_namespace, '/12345/test.jpg')
+ allow(File).to receive(:file?).with(file_path).and_return(true)
+ allow(File).to receive(:read).with(file_path).and_return(png)
+ end
+
+ subject { Notify.new_merge_request_email(merge_request_with_image.assignee_id, merge_request_with_image.id) }
+ it 'replaces attached images with inline images' do
+ is_expected.to have_body_text URI.encode(png_encoded)
end
end
@@ -307,24 +362,24 @@ describe Notify do
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(current_user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'has the correct subject' do
- should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end
it 'contains the name of the previous assignee' do
- should have_body_text /#{previous_assignee.name}/
+ is_expected.to have_body_text /#{previous_assignee.name}/
end
it 'contains the name of the new assignee' do
- should have_body_text /#{assignee.name}/
+ is_expected.to have_body_text /#{assignee.name}/
end
it 'contains a link to the merge request' do
- should have_body_text /#{project_merge_request_path project, merge_request}/
+ is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
end
end
@@ -336,24 +391,24 @@ describe Notify do
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(current_user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'has the correct subject' do
- should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/i
+ is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/i
end
it 'contains the new status' do
- should have_body_text /#{status}/i
+ is_expected.to have_body_text /#{status}/i
end
it 'contains the user name' do
- should have_body_text /#{current_user.name}/i
+ is_expected.to have_body_text /#{current_user.name}/i
end
it 'contains a link to the merge request' do
- should have_body_text /#{project_merge_request_path project, merge_request}/
+ is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
end
end
@@ -365,20 +420,20 @@ describe Notify do
it 'is sent as the merge author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(merge_author.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(merge_author.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'has the correct subject' do
- should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end
it 'contains the new status' do
- should have_body_text /merged/i
+ is_expected.to have_body_text /merged/i
end
it 'contains a link to the merge request' do
- should have_body_text /#{project_merge_request_path project, merge_request}/
+ is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
end
end
end
@@ -392,36 +447,39 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it 'has the correct subject' do
- should have_subject /Project was moved/
+ is_expected.to have_subject /Project was moved/
end
it 'contains name of project' do
- should have_body_text /#{project.name_with_namespace}/
+ is_expected.to have_body_text /#{project.name_with_namespace}/
end
it 'contains new user role' do
- should have_body_text /#{project.ssh_url_to_repo}/
+ is_expected.to have_body_text /#{project.ssh_url_to_repo}/
end
end
describe 'project access changed' do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:project_member) { create(:project_member,
- project: project,
- user: user) }
+ let(:project_member) do
+ create(:project_member,
+ project: project,
+ user: user)
+ end
+
subject { Notify.project_access_granted_email(project_member.id) }
it_behaves_like 'an email sent from GitLab'
it 'has the correct subject' do
- should have_subject /Access to project was granted/
+ is_expected.to have_subject /Access to project was granted/
end
it 'contains name of project' do
- should have_body_text /#{project.name}/
+ is_expected.to have_body_text /#{project.name}/
end
it 'contains new user role' do
- should have_body_text /#{project_member.human_access}/
+ is_expected.to have_body_text /#{project_member.human_access}/
end
end
@@ -430,29 +488,55 @@ describe Notify do
let(:note) { create(:note, project: project, author: note_author) }
before :each do
- Note.stub(:find).with(note.id).and_return(note)
+ allow(Note).to receive(:find).with(note.id).and_return(note)
end
shared_examples 'a note email' do
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(note_author.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(note_author.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'is sent to the given recipient' do
- should deliver_to recipient.email
+ is_expected.to deliver_to recipient.notification_email
end
it 'contains the message from the note' do
- should have_body_text /#{note.note}/
+ is_expected.to have_body_text /#{note.note}/
+ end
+ end
+
+ describe 'on a commit that contains an image' do
+ let(:commit) { project.repository.commit }
+ let(:note_with_image) do
+ create(:note,
+ project: project,
+ author: note_author,
+ note: "")
+ end
+
+ let(:png) {File.read("#{Rails.root}/spec/fixtures/dk.png")}
+ let(:png_encoded) { Base64::encode64(png) }
+
+ before :each do
+ file_path = File.join(Rails.root, 'public', 'uploads', note_with_image.project.path_with_namespace, '12345/test.jpg')
+ allow(File).to receive(:file?).with(file_path).and_return(true)
+ allow(File).to receive(:read).with(file_path).and_return(png)
+ allow(Note).to receive(:find).with(note_with_image.id).and_return(note_with_image)
+ allow(note_with_image).to receive(:noteable).and_return(commit)
+ end
+
+ subject { Notify.note_commit_email(recipient.id, note_with_image.id) }
+ it 'replaces attached images with inline images' do
+ is_expected.to have_body_text URI.encode(png_encoded)
end
end
describe 'on a commit' do
let(:commit) { project.repository.commit }
- before(:each) { note.stub(:noteable).and_return(commit) }
+ before(:each) { allow(note).to receive(:noteable).and_return(commit) }
subject { Notify.note_commit_email(recipient.id, note.id) }
@@ -460,18 +544,18 @@ describe Notify do
it_behaves_like 'an answer to an existing thread', 'commits'
it 'has the correct subject' do
- should have_subject /#{commit.title} \(#{commit.short_id}\)/
+ is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
end
it 'contains a link to the commit' do
- should have_body_text commit.short_id
+ is_expected.to have_body_text commit.short_id
end
end
describe 'on a merge request' do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") }
- before(:each) { note.stub(:noteable).and_return(merge_request) }
+ let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") }
+ before(:each) { allow(note).to receive(:noteable).and_return(merge_request) }
subject { Notify.note_merge_request_email(recipient.id, note.id) }
@@ -479,18 +563,18 @@ describe Notify do
it_behaves_like 'an answer to an existing thread', 'merge_request'
it 'has the correct subject' do
- should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end
it 'contains a link to the merge request note' do
- should have_body_text /#{note_on_merge_request_path}/
+ is_expected.to have_body_text /#{note_on_merge_request_path}/
end
end
describe 'on an issue' do
let(:issue) { create(:issue, project: project) }
- let(:note_on_issue_path) { project_issue_path(project, issue, anchor: "note_#{note.id}") }
- before(:each) { note.stub(:noteable).and_return(issue) }
+ let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") }
+ before(:each) { allow(note).to receive(:noteable).and_return(issue) }
subject { Notify.note_issue_email(recipient.id, note.id) }
@@ -498,11 +582,11 @@ describe Notify do
it_behaves_like 'an answer to an existing thread', 'issue'
it 'has the correct subject' do
- should have_subject /#{issue.title} \(##{issue.iid}\)/
+ is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
end
it 'contains a link to the issue note' do
- should have_body_text /#{note_on_issue_path}/
+ is_expected.to have_body_text /#{note_on_issue_path}/
end
end
end
@@ -518,15 +602,15 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it 'has the correct subject' do
- should have_subject /Access to group was granted/
+ is_expected.to have_subject /Access to group was granted/
end
it 'contains name of project' do
- should have_body_text /#{group.name}/
+ is_expected.to have_body_text /#{group.name}/
end
it 'contains new user role' do
- should have_body_text /#{membership.human_access}/
+ is_expected.to have_body_text /#{membership.human_access}/
end
end
@@ -544,15 +628,109 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it 'is sent to the new user' do
- should deliver_to 'new-email@mail.com'
+ is_expected.to deliver_to 'new-email@mail.com'
end
it 'has the correct subject' do
- should have_subject "Confirmation instructions"
+ is_expected.to have_subject "Confirmation instructions"
end
it 'includes a link to the site' do
- should have_body_text /#{example_site_path}/
+ is_expected.to have_body_text /#{example_site_path}/
+ end
+ end
+
+ describe 'email on push for a created branch' do
+ let(:example_site_path) { root_path }
+ let(:user) { create(:user) }
+ let(:tree_path) { namespace_project_tree_path(project.namespace, project, "master") }
+
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :create) }
+
+ it 'is sent as the author' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(user.name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'is sent to recipient' do
+ is_expected.to deliver_to 'devs@company.name'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /Pushed new branch master/
+ end
+
+ it 'contains a link to the branch' do
+ is_expected.to have_body_text /#{tree_path}/
+ end
+ end
+
+ describe 'email on push for a created tag' do
+ let(:example_site_path) { root_path }
+ let(:user) { create(:user) }
+ let(:tree_path) { namespace_project_tree_path(project.namespace, project, "v1.0") }
+
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
+
+ it 'is sent as the author' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(user.name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'is sent to recipient' do
+ is_expected.to deliver_to 'devs@company.name'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /Pushed new tag v1\.0/
+ end
+
+ it 'contains a link to the tag' do
+ is_expected.to have_body_text /#{tree_path}/
+ end
+ end
+
+ describe 'email on push for a deleted branch' do
+ let(:example_site_path) { root_path }
+ let(:user) { create(:user) }
+
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :delete) }
+
+ it 'is sent as the author' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(user.name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'is sent to recipient' do
+ is_expected.to deliver_to 'devs@company.name'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /Deleted branch master/
+ end
+ end
+
+ describe 'email on push for a deleted tag' do
+ let(:example_site_path) { root_path }
+ let(:user) { create(:user) }
+
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
+
+ it 'is sent as the author' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(user.name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'is sent to recipient' do
+ is_expected.to deliver_to 'devs@company.name'
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /Deleted tag v1\.0/
end
end
@@ -561,34 +739,87 @@ describe Notify do
let(:user) { create(:user) }
let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_image_commit.id, sample_commit.id) }
let(:commits) { Commit.decorate(compare.commits) }
- let(:diff_path) { project_compare_path(project, from: commits.first, to: commits.last) }
+ let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base), to: Commit.new(compare.head)) }
+ let(:send_from_committer_email) { false }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) }
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) }
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'is sent to recipient' do
- should deliver_to 'devs@company.name'
+ is_expected.to deliver_to 'devs@company.name'
end
it 'has the correct subject' do
- should have_subject /#{commits.length} new commits pushed to repository/
+ is_expected.to have_subject /\[#{project.path_with_namespace}\]\[master\] #{commits.length} commits:/
end
it 'includes commits list' do
- should have_body_text /Change some files/
+ is_expected.to have_body_text /Change some files/
end
it 'includes diffs' do
- should have_body_text /def archive_formats_regex/
+ is_expected.to have_body_text /def archive_formats_regex/
end
it 'contains a link to the diff' do
- should have_body_text /#{diff_path}/
+ is_expected.to have_body_text /#{diff_path}/
+ end
+
+ it 'doesn not contain the misleading footer' do
+ is_expected.not_to have_body_text /you are a member of/
+ end
+
+ context "when set to send from committer email if domain matches" do
+
+ let(:send_from_committer_email) { true }
+
+ before do
+ allow(Gitlab.config.gitlab).to receive(:host).and_return("gitlab.corp.company.com")
+ end
+
+ context "when the committer email domain is within the GitLab domain" do
+
+ before do
+ user.update_attribute(:email, "user@company.com")
+ user.confirm!
+ end
+
+ it "is sent from the committer email" do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.address).to eq(user.email)
+ end
+ end
+
+ context "when the committer email domain is not completely within the GitLab domain" do
+
+ before do
+ user.update_attribute(:email, "user@something.company.com")
+ user.confirm!
+ end
+
+ it "is sent from the default email" do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.address).to eq(gitlab_sender)
+ end
+ end
+
+ context "when the committer email domain is outside the GitLab domain" do
+
+ before do
+ user.update_attribute(:email, "user@mpany.com")
+ user.confirm!
+ end
+
+ it "is sent from the default email" do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.address).to eq(gitlab_sender)
+ end
+ end
end
end
@@ -597,34 +828,34 @@ describe Notify do
let(:user) { create(:user) }
let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_commit.parent_id, sample_commit.id) }
let(:commits) { Commit.decorate(compare.commits) }
- let(:diff_path) { project_commit_path(project, commits.first) }
+ let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) }
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) }
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
- sender.display_name.should eq(user.name)
- sender.address.should eq(gitlab_sender)
+ expect(sender.display_name).to eq(user.name)
+ expect(sender.address).to eq(gitlab_sender)
end
it 'is sent to recipient' do
- should deliver_to 'devs@company.name'
+ is_expected.to deliver_to 'devs@company.name'
end
it 'has the correct subject' do
- should have_subject /#{commits.first.title}/
+ is_expected.to have_subject /#{commits.first.title}/
end
it 'includes commits list' do
- should have_body_text /Change some files/
+ is_expected.to have_body_text /Change some files/
end
it 'includes diffs' do
- should have_body_text /def archive_formats_regex/
+ is_expected.to have_body_text /def archive_formats_regex/
end
it 'contains a link to the diff' do
- should have_body_text /#{diff_path}/
+ is_expected.to have_body_text /#{diff_path}/
end
end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b4f0b2c201aba6f638f0a3d6762a0efd9c1dff25
--- /dev/null
+++ b/spec/models/application_setting_spec.rb
@@ -0,0 +1,24 @@
+# == Schema Information
+#
+# Table name: application_settings
+#
+# id :integer not null, primary key
+# default_projects_limit :integer
+# default_branch_protection :integer
+# signup_enabled :boolean
+# signin_enabled :boolean
+# gravatar_enabled :boolean
+# sign_in_text :text
+# created_at :datetime
+# updated_at :datetime
+# home_page_url :string(255)
+# default_branch_protection :integer default(2)
+# twitter_sharing_enabled :boolean default(TRUE)
+# restricted_visibility_levels :text
+#
+
+require 'spec_helper'
+
+describe ApplicationSetting, models: true do
+ it { expect(ApplicationSetting.create_from_defaults).to be_valid }
+end
diff --git a/spec/models/assembla_service_spec.rb b/spec/models/assembla_service_spec.rb
deleted file mode 100644
index 4300090eb13ed4c2a03a861d32e9df7627840680..0000000000000000000000000000000000000000
--- a/spec/models/assembla_service_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe AssemblaService, models: true do
- describe "Associations" do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- before do
- @assembla_service = AssemblaService.new
- @assembla_service.stub(
- project_id: project.id,
- project: project,
- service_hook: true,
- token: 'verySecret',
- subdomain: 'project_name'
- )
- @sample_data = GitPushService.new.sample_data(project, user)
- @api_url = 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret'
- WebMock.stub_request(:post, @api_url)
- end
-
- it "should call Assembla API" do
- @assembla_service.execute(@sample_data)
- WebMock.should have_requested(:post, @api_url).with(
- body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
- ).once
- end
- end
-end
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index 0f31c407c90d3b2a7d6d82846c426a12e79a34e6..8ab72151a690b1e37b60cffe94e0742955907f48 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -18,22 +18,22 @@ require 'spec_helper'
describe BroadcastMessage do
subject { create(:broadcast_message) }
- it { should be_valid }
+ it { is_expected.to be_valid }
describe :current do
it "should return last message if time match" do
broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow)
- BroadcastMessage.current.should == broadcast_message
+ expect(BroadcastMessage.current).to eq(broadcast_message)
end
it "should return nil if time not come" do
broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days)
- BroadcastMessage.current.should be_nil
+ expect(BroadcastMessage.current).to be_nil
end
it "should return nil if time has passed" do
broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday)
- BroadcastMessage.current.should be_nil
+ expect(BroadcastMessage.current).to be_nil
end
end
end
diff --git a/spec/models/buildbox_service_spec.rb b/spec/models/buildbox_service_spec.rb
deleted file mode 100644
index 1d9ca51be16138ab31c50db3bd4a72cf4b4a1149..0000000000000000000000000000000000000000
--- a/spec/models/buildbox_service_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe BuildboxService do
- describe 'Associations' do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe 'commits methods' do
- before do
- @project = Project.new
- @project.stub(
- default_branch: 'default-brancho'
- )
-
- @service = BuildboxService.new
- @service.stub(
- project: @project,
- service_hook: true,
- project_url: 'https://buildbox.io/account-name/example-project',
- token: 'secret-sauce-webhook-token:secret-sauce-status-token'
- )
- end
-
- describe :webhook_url do
- it 'returns the webhook url' do
- @service.webhook_url.should ==
- 'https://webhook.buildbox.io/deliver/secret-sauce-webhook-token'
- end
- end
-
- describe :commit_status_path do
- it 'returns the correct status page' do
- @service.commit_status_path('2ab7834c').should ==
- 'https://gitlab.buildbox.io/status/secret-sauce-status-token.json?commit=2ab7834c'
- end
- end
-
- describe :build_page do
- it 'returns the correct build page' do
- @service.build_page('2ab7834c').should ==
- 'https://buildbox.io/account-name/example-project/builds?commit=2ab7834c'
- end
- end
-
- describe :builds_page do
- it 'returns the correct path to the builds page' do
- @service.builds_path.should ==
- 'https://buildbox.io/account-name/example-project/builds?branch=default-brancho'
- end
- end
-
- describe :status_img_path do
- it 'returns the correct path to the status image' do
- @service.status_img_path.should == 'https://badge.buildbox.io/secret-sauce-status-token.svg'
- end
- end
- end
-end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index a6ec44da4be170a709341991f3c7b3c0e24c8bd0..8b3d88640da75479752fce7553b3ecce851d3196 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -6,22 +6,22 @@ describe Commit do
describe '#title' do
it "returns no_commit_message when safe_message is blank" do
- commit.stub(:safe_message).and_return('')
- commit.title.should == "--no commit message"
+ allow(commit).to receive(:safe_message).and_return('')
+ expect(commit.title).to eq("--no commit message")
end
it "truncates a message without a newline at 80 characters" do
message = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit. Vivamus egestas lacinia lacus, sed rutrum mauris.'
- commit.stub(:safe_message).and_return(message)
- commit.title.should == "#{message[0..79]}…"
+ allow(commit).to receive(:safe_message).and_return(message)
+ expect(commit.title).to eq("#{message[0..79]}…")
end
it "truncates a message with a newline before 80 characters at the newline" do
message = commit.safe_message.split(" ").first
- commit.stub(:safe_message).and_return(message + "\n" + message)
- commit.title.should == message
+ allow(commit).to receive(:safe_message).and_return(message + "\n" + message)
+ expect(commit.title).to eq(message)
end
it "does not truncates a message with a newline after 80 but less 100 characters" do
@@ -30,25 +30,25 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis
Vivamus egestas lacinia lacus, sed rutrum mauris.
eos
- commit.stub(:safe_message).and_return(message)
- commit.title.should == message.split("\n").first
+ allow(commit).to receive(:safe_message).and_return(message)
+ expect(commit.title).to eq(message.split("\n").first)
end
end
describe "delegation" do
subject { commit }
- it { should respond_to(:message) }
- it { should respond_to(:authored_date) }
- it { should respond_to(:committed_date) }
- it { should respond_to(:committer_email) }
- it { should respond_to(:author_email) }
- it { should respond_to(:parents) }
- it { should respond_to(:date) }
- it { should respond_to(:diffs) }
- it { should respond_to(:tree) }
- it { should respond_to(:id) }
- it { should respond_to(:to_patch) }
+ it { is_expected.to respond_to(:message) }
+ it { is_expected.to respond_to(:authored_date) }
+ it { is_expected.to respond_to(:committed_date) }
+ it { is_expected.to respond_to(:committer_email) }
+ it { is_expected.to respond_to(:author_email) }
+ it { is_expected.to respond_to(:parents) }
+ it { is_expected.to respond_to(:date) }
+ it { is_expected.to respond_to(:diffs) }
+ it { is_expected.to respond_to(:tree) }
+ it { is_expected.to respond_to(:id) }
+ it { is_expected.to respond_to(:to_patch) }
end
describe '#closes_issues' do
@@ -57,18 +57,14 @@ eos
let(:other_issue) { create :issue, project: other_project }
it 'detects issues that this commit is marked as closing' do
- stub_const('Gitlab::ClosingIssueExtractor::ISSUE_CLOSING_REGEX',
- /Fixes #\d+/)
commit.stub(safe_message: "Fixes ##{issue.iid}")
- commit.closes_issues(project).should == [issue]
+ expect(commit.closes_issues(project)).to eq([issue])
end
it 'does not detect issues from other projects' do
ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
- stub_const('Gitlab::ClosingIssueExtractor::ISSUE_CLOSING_REGEX',
- /^([Cc]loses|[Ff]ixes)/)
commit.stub(safe_message: "Fixes #{ext_ref}")
- commit.closes_issues(project).should be_empty
+ expect(commit.closes_issues(project)).to be_empty
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 9cbc899067689d4f92056367995f8e92d9cc92df..557c71b4d2cecc2c8a28f2a57633fee4ef7051f3 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -4,63 +4,63 @@ describe Issue, "Issuable" do
let(:issue) { create(:issue) }
describe "Associations" do
- it { should belong_to(:project) }
- it { should belong_to(:author) }
- it { should belong_to(:assignee) }
- it { should have_many(:notes).dependent(:destroy) }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:author) }
+ it { is_expected.to belong_to(:assignee) }
+ it { is_expected.to have_many(:notes).dependent(:destroy) }
end
describe "Validation" do
before { subject.stub(set_iid: false) }
- it { should validate_presence_of(:project) }
- it { should validate_presence_of(:iid) }
- it { should validate_presence_of(:author) }
- it { should validate_presence_of(:title) }
- it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) }
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:iid) }
+ it { is_expected.to validate_presence_of(:author) }
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to ensure_length_of(:title).is_at_least(0).is_at_most(255) }
end
describe "Scope" do
- it { described_class.should respond_to(:opened) }
- it { described_class.should respond_to(:closed) }
- it { described_class.should respond_to(:assigned) }
+ it { expect(described_class).to respond_to(:opened) }
+ it { expect(described_class).to respond_to(:closed) }
+ it { expect(described_class).to respond_to(:assigned) }
end
describe ".search" do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
it "matches by title" do
- described_class.search('able').should == [searchable_issue]
+ expect(described_class.search('able')).to eq([searchable_issue])
end
end
describe "#today?" do
it "returns true when created today" do
# Avoid timezone differences and just return exactly what we want
- Date.stub(:today).and_return(issue.created_at.to_date)
- issue.today?.should be_true
+ allow(Date).to receive(:today).and_return(issue.created_at.to_date)
+ expect(issue.today?).to be_truthy
end
it "returns false when not created today" do
- Date.stub(:today).and_return(Date.yesterday)
- issue.today?.should be_false
+ allow(Date).to receive(:today).and_return(Date.yesterday)
+ expect(issue.today?).to be_falsey
end
end
describe "#new?" do
it "returns true when created today and record hasn't been updated" do
- issue.stub(:today?).and_return(true)
- issue.new?.should be_true
+ allow(issue).to receive(:today?).and_return(true)
+ expect(issue.new?).to be_truthy
end
it "returns false when not created today" do
- issue.stub(:today?).and_return(false)
- issue.new?.should be_false
+ allow(issue).to receive(:today?).and_return(false)
+ expect(issue.new?).to be_falsey
end
it "returns false when record has been updated" do
- issue.stub(:today?).and_return(true)
+ allow(issue).to receive(:today?).and_return(true)
issue.touch
- issue.new?.should be_false
+ expect(issue.new?).to be_falsey
end
end
end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index ca6f11b2a4dcb9295b33a282e0eccf614d01635f..eadb941a3fa8780eb5789e890760c9661295c89d 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -8,7 +8,7 @@ describe Issue, "Mentionable" do
subject { issue.mentioned_users }
- it { should include(user) }
- it { should_not include(user2) }
+ it { is_expected.to include(user) }
+ it { is_expected.not_to include(user2) }
end
end
diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb
index adbbbac875fa5199a4fbf76e2a8640f578c008e7..b32be8d7a7cb45199703192cf0c8d2f4fbcd8d55 100644
--- a/spec/models/deploy_key_spec.rb
+++ b/spec/models/deploy_key_spec.rb
@@ -19,7 +19,7 @@ describe DeployKey do
let(:deploy_key) { create(:deploy_key, projects: [project]) }
describe "Associations" do
- it { should have_many(:deploy_keys_projects) }
- it { should have_many(:projects) }
+ it { is_expected.to have_many(:deploy_keys_projects) }
+ it { is_expected.to have_many(:projects) }
end
end
diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb
index 3e0e25ee39aff4d4804ce8db4da89c61959aa8e4..f351aab923831598f2bc1785aa85ffe264919833 100644
--- a/spec/models/deploy_keys_project_spec.rb
+++ b/spec/models/deploy_keys_project_spec.rb
@@ -13,12 +13,45 @@ require 'spec_helper'
describe DeployKeysProject do
describe "Associations" do
- it { should belong_to(:deploy_key) }
- it { should belong_to(:project) }
+ it { is_expected.to belong_to(:deploy_key) }
+ it { is_expected.to belong_to(:project) }
end
describe "Validation" do
- it { should validate_presence_of(:project_id) }
- it { should validate_presence_of(:deploy_key_id) }
+ it { is_expected.to validate_presence_of(:project_id) }
+ it { is_expected.to validate_presence_of(:deploy_key_id) }
+ end
+
+ describe "Destroying" do
+ let(:project) { create(:project) }
+ subject { create(:deploy_keys_project, project: project) }
+ let(:deploy_key) { subject.deploy_key }
+
+ context "when the deploy key is only used by this project" do
+ it "destroys the deploy key" do
+ subject.destroy
+
+ expect {
+ deploy_key.reload
+ }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context "when the deploy key is used by more than one project" do
+
+ let!(:other_project) { create(:project) }
+
+ before do
+ other_project.deploy_keys << deploy_key
+ end
+
+ it "doesn't destroy the deploy key" do
+ subject.destroy
+
+ expect {
+ deploy_key.reload
+ }.not_to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
end
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 204ae9da7042bc89fe1dc4b6863d3e0d16f57e50..0f32f162a104b70ff3d124b0cf9fcece128b6b94 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -18,16 +18,16 @@ require 'spec_helper'
describe Event do
describe "Associations" do
- it { should belong_to(:project) }
- it { should belong_to(:target) }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:target) }
end
describe "Respond to" do
- it { should respond_to(:author_name) }
- it { should respond_to(:author_email) }
- it { should respond_to(:issue_title) }
- it { should respond_to(:merge_request_title) }
- it { should respond_to(:commits) }
+ it { is_expected.to respond_to(:author_name) }
+ it { is_expected.to respond_to(:author_email) }
+ it { is_expected.to respond_to(:issue_title) }
+ it { is_expected.to respond_to(:merge_request_title) }
+ it { is_expected.to respond_to(:commits) }
end
describe "Push event" do
@@ -58,10 +58,10 @@ describe Event do
)
end
- it { @event.push?.should be_true }
- it { @event.proper?.should be_true }
- it { @event.tag?.should be_false }
- it { @event.branch_name.should == "master" }
- it { @event.author.should == @user }
+ it { expect(@event.push?).to be_truthy }
+ it { expect(@event.proper?).to be_truthy }
+ it { expect(@event.tag?).to be_falsey }
+ it { expect(@event.branch_name).to eq("master") }
+ it { expect(@event.author).to eq(@user) }
end
end
diff --git a/spec/models/external_wiki_service_spec.rb b/spec/models/external_wiki_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..78ef687d29cbeec644b45d8cc267e782939ae802
--- /dev/null
+++ b/spec/models/external_wiki_service_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe ExternalWikiService do
+ include ExternalWikiHelper
+ describe "Associations" do
+ it { should belong_to :project }
+ it { should have_one :service_hook }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { should validate_presence_of :external_wiki_url }
+ end
+ end
+
+ describe 'External wiki' do
+ let(:project) { create(:project) }
+
+ context 'when it is active' do
+ before do
+ properties = { 'external_wiki_url' => 'https://gitlab.com' }
+ @service = project.create_external_wiki_service(active: true, properties: properties)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should replace the wiki url' do
+ wiki_path = get_project_wiki_path(project)
+ wiki_path.should match('https://gitlab.com')
+ end
+ end
+ end
+end
diff --git a/spec/models/flowdock_service_spec.rb b/spec/models/flowdock_service_spec.rb
deleted file mode 100644
index 5540f0fa988809b0d384cda283a0502d275d31f5..0000000000000000000000000000000000000000
--- a/spec/models/flowdock_service_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe FlowdockService do
- describe "Associations" do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- before do
- @flowdock_service = FlowdockService.new
- @flowdock_service.stub(
- project_id: project.id,
- project: project,
- service_hook: true,
- token: 'verySecret'
- )
- @sample_data = GitPushService.new.sample_data(project, user)
- @api_url = 'https://api.flowdock.com/v1/git/verySecret'
- WebMock.stub_request(:post, @api_url)
- end
-
- it "should call FlowDock API" do
- @flowdock_service.execute(@sample_data)
- WebMock.should have_requested(:post, @api_url).with(
- body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
- ).once
- end
- end
-end
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
index 1845c6103f5a4efc33dda69224995ba379d0a49b..7d0ad44a92c2ad1f4c072d6c3bc4bc81df6d3097 100644
--- a/spec/models/forked_project_link_spec.rb
+++ b/spec/models/forked_project_link_spec.rb
@@ -21,11 +21,11 @@ describe ForkedProjectLink, "add link on fork" do
end
it "project_to should know it is forked" do
- @project_to.forked?.should be_true
+ expect(@project_to.forked?).to be_truthy
end
it "project should know who it is forked from" do
- @project_to.forked_from_project.should == project_from
+ expect(@project_to.forked_from_project).to eq(project_from)
end
end
@@ -43,15 +43,15 @@ describe :forked_from_project do
it "project_to should know it is forked" do
- project_to.forked?.should be_true
+ expect(project_to.forked?).to be_truthy
end
it "project_from should not be forked" do
- project_from.forked?.should be_false
+ expect(project_from.forked?).to be_falsey
end
it "project_to.destroy should destroy fork_link" do
- forked_project_link.should_receive(:destroy)
+ expect(forked_project_link).to receive(:destroy)
project_to.destroy
end
diff --git a/spec/models/gemnasium_service_spec.rb b/spec/models/gemnasium_service_spec.rb
deleted file mode 100644
index 60ffa6f8b05766f225d755d0b7bdfa7e0e6521b4..0000000000000000000000000000000000000000
--- a/spec/models/gemnasium_service_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe GemnasiumService do
- describe "Associations" do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- before do
- @gemnasium_service = GemnasiumService.new
- @gemnasium_service.stub(
- project_id: project.id,
- project: project,
- service_hook: true,
- token: 'verySecret',
- api_key: 'GemnasiumUserApiKey'
- )
- @sample_data = GitPushService.new.sample_data(project, user)
- end
- it "should call Gemnasium service" do
- Gemnasium::GitlabService.should_receive(:execute).with(an_instance_of(Hash)).once
- @gemnasium_service.execute(@sample_data)
- end
- end
-end
diff --git a/spec/models/gitlab_ci_service_spec.rb b/spec/models/gitlab_ci_service_spec.rb
deleted file mode 100644
index 83277058fbb183a838d3fc96a564a5c6feb68b51..0000000000000000000000000000000000000000
--- a/spec/models/gitlab_ci_service_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe GitlabCiService do
- describe "Associations" do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe "Mass assignment" do
- end
-
- describe 'commits methods' do
- before do
- @service = GitlabCiService.new
- @service.stub(
- service_hook: true,
- project_url: 'http://ci.gitlab.org/projects/2',
- token: 'verySecret'
- )
- end
-
- describe :commit_status_path do
- it { @service.commit_status_path("2ab7834c").should == "http://ci.gitlab.org/projects/2/commits/2ab7834c/status.json?token=verySecret"}
- end
-
- describe :build_page do
- it { @service.build_page("2ab7834c").should == "http://ci.gitlab.org/projects/2/commits/2ab7834c"}
- end
- end
-end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 1d4ba8a2b850cdae3075518d52749828e7a9246a..9428224a64f9eba52a98ba773663002d1f4b3c0c 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -19,29 +19,29 @@ describe Group do
let!(:group) { create(:group) }
describe "Associations" do
- it { should have_many :projects }
- it { should have_many :group_members }
+ it { is_expected.to have_many :projects }
+ it { is_expected.to have_many :group_members }
end
- it { should validate_presence_of :name }
- it { should validate_uniqueness_of(:name) }
- it { should validate_presence_of :path }
- it { should validate_uniqueness_of(:path) }
- it { should_not validate_presence_of :owner }
+ it { is_expected.to validate_presence_of :name }
+ it { is_expected.to validate_uniqueness_of(:name) }
+ it { is_expected.to validate_presence_of :path }
+ it { is_expected.to validate_uniqueness_of(:path) }
+ it { is_expected.not_to validate_presence_of :owner }
describe :users do
- it { group.users.should == group.owners }
+ it { expect(group.users).to eq(group.owners) }
end
describe :human_name do
- it { group.human_name.should == group.name }
+ it { expect(group.human_name).to eq(group.name) }
end
describe :add_users do
let(:user) { create(:user) }
before { group.add_user(user, GroupMember::MASTER) }
- it { group.group_members.masters.map(&:user).should include(user) }
+ it { expect(group.group_members.masters.map(&:user)).to include(user) }
end
describe :add_users do
@@ -49,10 +49,10 @@ describe Group do
before { group.add_users([user.id], GroupMember::GUEST) }
it "should update the group permission" do
- group.group_members.guests.map(&:user).should include(user)
+ expect(group.group_members.guests.map(&:user)).to include(user)
group.add_users([user.id], GroupMember::DEVELOPER)
- group.group_members.developers.map(&:user).should include(user)
- group.group_members.guests.map(&:user).should_not include(user)
+ expect(group.group_members.developers.map(&:user)).to include(user)
+ expect(group.group_members.guests.map(&:user)).not_to include(user)
end
end
@@ -62,12 +62,12 @@ describe Group do
it "should be true if avatar is image" do
group.update_attribute(:avatar, 'uploads/avatar.png')
- group.avatar_type.should be_true
+ expect(group.avatar_type).to be_truthy
end
it "should be false if avatar is html page" do
group.update_attribute(:avatar, 'uploads/avatar.html')
- group.avatar_type.should == ["only images allowed"]
+ expect(group.avatar_type).to eq(["only images allowed"])
end
end
end
diff --git a/spec/models/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
similarity index 100%
rename from spec/models/project_hook_spec.rb
rename to spec/models/hooks/project_hook_spec.rb
diff --git a/spec/models/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
similarity index 94%
rename from spec/models/service_hook_spec.rb
rename to spec/models/hooks/service_hook_spec.rb
index 6ec82438dfe6bae49bb759df5c936a9fa899cf7a..96bf74d45da7589495953ae9ca71598c8cd0d66d 100644
--- a/spec/models/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -19,6 +19,6 @@ require "spec_helper"
describe ServiceHook do
describe "Associations" do
- it { should belong_to :service }
+ it { is_expected.to belong_to :service }
end
end
diff --git a/spec/models/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
similarity index 50%
rename from spec/models/system_hook_spec.rb
rename to spec/models/hooks/system_hook_spec.rb
index 4ab5261dc9d1397e4044973e823575c966e26a01..810b311a40bbc24d60af228bf9a4aa57ec34d375 100644
--- a/spec/models/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -26,32 +26,32 @@ describe SystemHook do
it "project_create hook" do
Projects::CreateService.new(create(:user), name: 'empty').execute
- WebMock.should have_requested(:post, @system_hook.url).with(body: /project_create/).once
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /project_create/).once
end
it "project_destroy hook" do
user = create(:user)
project = create(:empty_project, namespace: user.namespace)
Projects::DestroyService.new(project, user, {}).execute
- WebMock.should have_requested(:post, @system_hook.url).with(body: /project_destroy/).once
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /project_destroy/).once
end
it "user_create hook" do
create(:user)
- WebMock.should have_requested(:post, @system_hook.url).with(body: /user_create/).once
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_create/).once
end
it "user_destroy hook" do
user = create(:user)
user.destroy
- WebMock.should have_requested(:post, @system_hook.url).with(body: /user_destroy/).once
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_destroy/).once
end
it "project_create hook" do
user = create(:user)
project = create(:project)
project.team << [user, :master]
- WebMock.should have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once
end
it "project_destroy hook" do
@@ -59,7 +59,42 @@ describe SystemHook do
project = create(:project)
project.team << [user, :master]
project.project_members.destroy_all
- WebMock.should have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once
end
+
+ it 'group create hook' do
+ create(:group)
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(
+ body: /group_create/
+ ).once
+ end
+
+ it 'group destroy hook' do
+ group = create(:group)
+ group.destroy
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(
+ body: /group_destroy/
+ ).once
+ end
+
+ it 'group member create hook' do
+ group = create(:group)
+ user = create(:user)
+ group.add_user(user, Gitlab::Access::MASTER)
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(
+ body: /user_add_to_group/
+ ).once
+ end
+
+ it 'group member destroy hook' do
+ group = create(:group)
+ user = create(:user)
+ group.add_user(user, Gitlab::Access::MASTER)
+ group.group_members.destroy_all
+ expect(WebMock).to have_requested(:post, @system_hook.url).with(
+ body: /user_remove_from_group/
+ ).once
+ end
+
end
end
diff --git a/spec/models/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
similarity index 59%
rename from spec/models/web_hook_spec.rb
rename to spec/models/hooks/web_hook_spec.rb
index e9c04ee89cbb1f3c453de5e788a2230592b6a29b..67ec9193ad7db5daa111c11a1fbcfca249bd1567 100644
--- a/spec/models/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -19,25 +19,25 @@ require 'spec_helper'
describe ProjectHook do
describe "Associations" do
- it { should belong_to :project }
+ it { is_expected.to belong_to :project }
end
describe "Mass assignment" do
end
describe "Validations" do
- it { should validate_presence_of(:url) }
+ it { is_expected.to validate_presence_of(:url) }
context "url format" do
- it { should allow_value("http://example.com").for(:url) }
- it { should allow_value("https://excample.com").for(:url) }
- it { should allow_value("http://test.com/api").for(:url) }
- it { should allow_value("http://test.com/api?key=abc").for(:url) }
- it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) }
+ it { is_expected.to allow_value("http://example.com").for(:url) }
+ it { is_expected.to allow_value("https://excample.com").for(:url) }
+ it { is_expected.to allow_value("http://test.com/api").for(:url) }
+ it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) }
+ it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) }
- it { should_not allow_value("example.com").for(:url) }
- it { should_not allow_value("ftp://example.com").for(:url) }
- it { should_not allow_value("herp-and-derp").for(:url) }
+ it { is_expected.not_to allow_value("example.com").for(:url) }
+ it { is_expected.not_to allow_value("ftp://example.com").for(:url) }
+ it { is_expected.not_to allow_value("herp-and-derp").for(:url) }
end
end
@@ -53,22 +53,22 @@ describe ProjectHook do
it "POSTs to the web hook URL" do
@project_hook.execute(@data)
- WebMock.should have_requested(:post, @project_hook.url).once
+ expect(WebMock).to have_requested(:post, @project_hook.url).once
end
it "POSTs the data as JSON" do
json = @data.to_json
@project_hook.execute(@data)
- WebMock.should have_requested(:post, @project_hook.url).with(body: json).once
+ expect(WebMock).to have_requested(:post, @project_hook.url).with(body: json).once
end
it "catches exceptions" do
- WebHook.should_receive(:post).and_raise("Some HTTP Post error")
+ expect(WebHook).to receive(:post).and_raise("Some HTTP Post error")
- lambda {
+ expect {
@project_hook.execute(@data)
- }.should raise_error
+ }.to raise_error
end
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 6b6efe832e5ebe8edb5c5fe179fc3aa09902eaa7..087e40c3d840e86faa9ca49049282d6d2cc1d0b3 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -21,14 +21,14 @@ require 'spec_helper'
describe Issue do
describe "Associations" do
- it { should belong_to(:milestone) }
+ it { is_expected.to belong_to(:milestone) }
end
describe "Mass assignment" do
end
describe 'modules' do
- it { should include_module(Issuable) }
+ it { is_expected.to include_module(Issuable) }
end
subject { create(:issue) }
@@ -36,10 +36,10 @@ describe Issue do
describe '#is_being_reassigned?' do
it 'returns true if the issue assignee has changed' do
subject.assignee = create(:user)
- subject.is_being_reassigned?.should be_true
+ expect(subject.is_being_reassigned?).to be_truthy
end
it 'returns false if the issue assignee has not changed' do
- subject.is_being_reassigned?.should be_false
+ expect(subject.is_being_reassigned?).to be_falsey
end
end
@@ -51,7 +51,7 @@ describe Issue do
issue = create :issue, assignee: user
end
- Issue.open_for(user).count.should eq 2
+ expect(Issue.open_for(user).count).to eq 2
end
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 95c0aed0ffe50da9b089546cd6e8211b0460a3f2..a212b95a7d6e9c5a645dd8bafdbf34e12bd26e1f 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -16,67 +16,67 @@ require 'spec_helper'
describe Key do
describe "Associations" do
- it { should belong_to(:user) }
+ it { is_expected.to belong_to(:user) }
end
describe "Mass assignment" do
end
describe "Validation" do
- it { should validate_presence_of(:title) }
- it { should validate_presence_of(:key) }
- it { should ensure_length_of(:title).is_within(0..255) }
- it { should ensure_length_of(:key).is_within(0..5000) }
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_presence_of(:key) }
+ it { is_expected.to ensure_length_of(:title).is_within(0..255) }
+ it { is_expected.to ensure_length_of(:key).is_within(0..5000) }
end
describe "Methods" do
- it { should respond_to :projects }
+ it { is_expected.to respond_to :projects }
end
context "validation of uniqueness" do
let(:user) { create(:user) }
it "accepts the key once" do
- build(:key, user: user).should be_valid
+ expect(build(:key, user: user)).to be_valid
end
it "does not accept the exact same key twice" do
create(:key, user: user)
- build(:key, user: user).should_not be_valid
+ expect(build(:key, user: user)).not_to be_valid
end
it "does not accept a duplicate key with a different comment" do
create(:key, user: user)
duplicate = build(:key, user: user)
duplicate.key << ' extra comment'
- duplicate.should_not be_valid
+ expect(duplicate).not_to be_valid
end
end
context "validate it is a fingerprintable key" do
it "accepts the fingerprintable key" do
- build(:key).should be_valid
+ expect(build(:key)).to be_valid
end
it "rejects the unfingerprintable key (contains space in middle)" do
- build(:key_with_a_space_in_the_middle).should_not be_valid
+ expect(build(:key_with_a_space_in_the_middle)).not_to be_valid
end
it "rejects the unfingerprintable key (not a key)" do
- build(:invalid_key).should_not be_valid
+ expect(build(:invalid_key)).not_to be_valid
end
end
context 'callbacks' do
it 'should add new key to authorized_file' do
@key = build(:personal_key, id: 7)
- GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key)
+ expect(GitlabShellWorker).to receive(:perform_async).with(:add_key, @key.shell_id, @key.key)
@key.save
end
it 'should remove key from authorized_file' do
@key = create(:personal_key)
- GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)
+ expect(GitlabShellWorker).to receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)
@key.destroy
end
end
diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb
index 0db60432ad37744c4c24db9c47d50367e72e5cbd..8c240826582807ad8614fc75602e4d9e7f2a0f9b 100644
--- a/spec/models/label_link_spec.rb
+++ b/spec/models/label_link_spec.rb
@@ -14,8 +14,8 @@ require 'spec_helper'
describe LabelLink do
let(:label) { create(:label_link) }
- it { label.should be_valid }
+ it { expect(label).to be_valid }
- it { should belong_to(:label) }
- it { should belong_to(:target) }
+ it { is_expected.to belong_to(:label) }
+ it { is_expected.to belong_to(:target) }
end
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 31634648f0418d45713e4ebd7fce8b5d55be3f97..8644ac46605af0a17c6e4c43e3254a510d9f971b 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -14,30 +14,30 @@ require 'spec_helper'
describe Label do
let(:label) { create(:label) }
- it { label.should be_valid }
+ it { expect(label).to be_valid }
- it { should belong_to(:project) }
+ it { is_expected.to belong_to(:project) }
describe 'Validation' do
it 'should validate color code' do
- build(:label, color: 'G-ITLAB').should_not be_valid
- build(:label, color: 'AABBCC').should_not be_valid
- build(:label, color: '#AABBCCEE').should_not be_valid
- build(:label, color: '#GGHHII').should_not be_valid
- build(:label, color: '#').should_not be_valid
- build(:label, color: '').should_not be_valid
+ expect(build(:label, color: 'G-ITLAB')).not_to be_valid
+ expect(build(:label, color: 'AABBCC')).not_to be_valid
+ expect(build(:label, color: '#AABBCCEE')).not_to be_valid
+ expect(build(:label, color: '#GGHHII')).not_to be_valid
+ expect(build(:label, color: '#')).not_to be_valid
+ expect(build(:label, color: '')).not_to be_valid
- build(:label, color: '#AABBCC').should be_valid
+ expect(build(:label, color: '#AABBCC')).to be_valid
end
it 'should validate title' do
- build(:label, title: 'G,ITLAB').should_not be_valid
- build(:label, title: 'G?ITLAB').should_not be_valid
- build(:label, title: 'G&ITLAB').should_not be_valid
- build(:label, title: '').should_not be_valid
+ expect(build(:label, title: 'G,ITLAB')).not_to be_valid
+ expect(build(:label, title: 'G?ITLAB')).not_to be_valid
+ expect(build(:label, title: 'G&ITLAB')).not_to be_valid
+ expect(build(:label, title: '')).not_to be_valid
- build(:label, title: 'GITLAB').should be_valid
- build(:label, title: 'gitlab').should be_valid
+ expect(build(:label, title: 'GITLAB')).to be_valid
+ expect(build(:label, title: 'gitlab')).to be_valid
end
end
end
diff --git a/spec/models/group_member_spec.rb b/spec/models/members/group_member_spec.rb
similarity index 67%
rename from spec/models/group_member_spec.rb
rename to spec/models/members/group_member_spec.rb
index 38657de6793be7af5a2ca230607d2a2ae422c4b5..e206c11f33ae43bb117f1d899307cd60de1a8e08 100644
--- a/spec/models/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -21,25 +21,25 @@ describe GroupMember do
it "should send email to user" do
membership = build(:group_member)
membership.stub(notification_service: double('NotificationService').as_null_object)
- membership.should_receive(:notification_service)
+ expect(membership).to receive(:notification_service)
membership.save
end
end
describe "#after_update" do
before do
- @membership = create :group_member
- @membership.stub(notification_service: double('NotificationService').as_null_object)
+ @group_member = create :group_member
+ @group_member.stub(notification_service: double('NotificationService').as_null_object)
end
it "should send email to user" do
- @membership.should_receive(:notification_service)
- @membership.update_attribute(:access_level, GroupMember::MASTER)
+ expect(@group_member).to receive(:notification_service)
+ @group_member.update_attribute(:access_level, GroupMember::MASTER)
end
it "does not send an email when the access level has not changed" do
- @membership.should_not_receive(:notification_service)
- @membership.update_attribute(:access_level, GroupMember::OWNER)
+ expect(@group_member).not_to receive(:notification_service)
+ @group_member.update_attribute(:access_level, GroupMember::OWNER)
end
end
end
diff --git a/spec/models/project_member_spec.rb b/spec/models/members/project_member_spec.rb
similarity index 69%
rename from spec/models/project_member_spec.rb
rename to spec/models/members/project_member_spec.rb
index 9b5f89b6d7d472f6deca96bbb26cd4f863ac0bbf..521721f3577ee2cba82abdc7c9b4a88fe3131ee7 100644
--- a/spec/models/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -33,19 +33,19 @@ describe ProjectMember do
@status = @project_2.team.import(@project_1)
end
- it { @status.should be_true }
+ it { expect(@status).to be_truthy }
describe 'project 2 should get user 1 as developer. user_2 should not be changed' do
- it { @project_2.users.should include(@user_1) }
- it { @project_2.users.should include(@user_2) }
+ it { expect(@project_2.users).to include(@user_1) }
+ it { expect(@project_2.users).to include(@user_2) }
- it { @abilities.allowed?(@user_1, :write_project, @project_2).should be_true }
- it { @abilities.allowed?(@user_2, :read_project, @project_2).should be_true }
+ it { expect(@abilities.allowed?(@user_1, :write_project, @project_2)).to be_truthy }
+ it { expect(@abilities.allowed?(@user_2, :read_project, @project_2)).to be_truthy }
end
describe 'project 1 should not be changed' do
- it { @project_1.users.should include(@user_1) }
- it { @project_1.users.should_not include(@user_2) }
+ it { expect(@project_1.users).to include(@user_1) }
+ it { expect(@project_1.users).not_to include(@user_2) }
end
end
@@ -64,12 +64,12 @@ describe ProjectMember do
)
end
- it { @project_1.users.should include(@user_1) }
- it { @project_1.users.should include(@user_2) }
+ it { expect(@project_1.users).to include(@user_1) }
+ it { expect(@project_1.users).to include(@user_2) }
- it { @project_2.users.should include(@user_1) }
- it { @project_2.users.should include(@user_2) }
+ it { expect(@project_2.users).to include(@user_1) }
+ it { expect(@project_2.users).to include(@user_2) }
end
describe :truncate_teams do
@@ -86,7 +86,7 @@ describe ProjectMember do
ProjectMember.truncate_teams([@project_1.id, @project_2.id])
end
- it { @project_1.users.should be_empty }
- it { @project_2.users.should be_empty }
+ it { expect(@project_1.users).to be_empty }
+ it { expect(@project_2.users).to be_empty }
end
end
diff --git a/spec/models/members_spec.rb b/spec/models/members_spec.rb
index 6866c4794c28b9c8774077c005b9ed73ac8cc57e..dfd3f7feb6b17a4b71caac66b0f3c1415bc4f8b1 100644
--- a/spec/models/members_spec.rb
+++ b/spec/models/members_spec.rb
@@ -2,19 +2,19 @@ require 'spec_helper'
describe Member do
describe "Associations" do
- it { should belong_to(:user) }
+ it { is_expected.to belong_to(:user) }
end
describe "Validation" do
subject { Member.new(access_level: Member::GUEST) }
- it { should validate_presence_of(:user) }
- it { should validate_presence_of(:source) }
- it { should ensure_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
+ it { is_expected.to validate_presence_of(:user) }
+ it { is_expected.to validate_presence_of(:source) }
+ it { is_expected.to validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
end
describe "Delegate methods" do
- it { should respond_to(:user_name) }
- it { should respond_to(:user_email) }
+ it { is_expected.to respond_to(:user_name) }
+ it { is_expected.to respond_to(:user_email) }
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 7b0d261d72feaf880304b5a78b9225e554238f48..d40503d791cad7a6c6f8ab9a8b4fbd18a933020b 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -18,41 +18,42 @@
# iid :integer
# description :text
# position :integer default(0)
+# locked_at :datetime
#
require 'spec_helper'
describe MergeRequest do
describe "Validation" do
- it { should validate_presence_of(:target_branch) }
- it { should validate_presence_of(:source_branch) }
+ it { is_expected.to validate_presence_of(:target_branch) }
+ it { is_expected.to validate_presence_of(:source_branch) }
end
describe "Mass assignment" do
end
describe "Respond to" do
- it { should respond_to(:unchecked?) }
- it { should respond_to(:can_be_merged?) }
- it { should respond_to(:cannot_be_merged?) }
+ it { is_expected.to respond_to(:unchecked?) }
+ it { is_expected.to respond_to(:can_be_merged?) }
+ it { is_expected.to respond_to(:cannot_be_merged?) }
end
describe 'modules' do
- it { should include_module(Issuable) }
+ it { is_expected.to include_module(Issuable) }
end
describe "#mr_and_commit_notes" do
let!(:merge_request) { create(:merge_request) }
before do
- merge_request.stub(:commits) { [merge_request.source_project.repository.commit] }
+ allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] }
create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.project)
create(:note, noteable: merge_request, project: merge_request.project)
end
it "should include notes for commits" do
- merge_request.commits.should_not be_empty
- merge_request.mr_and_commit_notes.count.should == 2
+ expect(merge_request.commits).not_to be_empty
+ expect(merge_request.mr_and_commit_notes.count).to eq(2)
end
end
@@ -61,10 +62,10 @@ describe MergeRequest do
describe '#is_being_reassigned?' do
it 'returns true if the merge_request assignee has changed' do
subject.assignee = create(:user)
- subject.is_being_reassigned?.should be_true
+ expect(subject.is_being_reassigned?).to be_truthy
end
it 'returns false if the merge request assignee has not changed' do
- subject.is_being_reassigned?.should be_false
+ expect(subject.is_being_reassigned?).to be_falsey
end
end
@@ -73,11 +74,11 @@ describe MergeRequest do
subject.source_project = create(:project, namespace: create(:group))
subject.target_project = create(:project, namespace: create(:group))
- subject.for_fork?.should be_true
+ expect(subject.for_fork?).to be_truthy
end
it 'returns false if is not for a fork' do
- subject.for_fork?.should be_false
+ expect(subject.for_fork?).to be_falsey
end
end
@@ -95,14 +96,14 @@ describe MergeRequest do
it 'accesses the set of issues that will be closed on acceptance' do
subject.project.stub(default_branch: subject.target_branch)
- subject.closes_issues.should == [issue0, issue1].sort_by(&:id)
+ expect(subject.closes_issues).to eq([issue0, issue1].sort_by(&:id))
end
it 'only lists issues as to be closed if it targets the default branch' do
subject.project.stub(default_branch: 'master')
subject.target_branch = 'something-else'
- subject.closes_issues.should be_empty
+ expect(subject.closes_issues).to be_empty
end
it 'detects issues mentioned in the description' do
@@ -110,7 +111,7 @@ describe MergeRequest do
subject.description = "Closes ##{issue2.iid}"
subject.project.stub(default_branch: subject.target_branch)
- subject.closes_issues.should include(issue2)
+ expect(subject.closes_issues).to include(issue2)
end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index a3071c3251a245d921810b51ca26f593c3187842..45171e1bf6459a35e4b7c0a4e227cdfc2b9d69f4 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -17,8 +17,8 @@ require 'spec_helper'
describe Milestone do
describe "Associations" do
- it { should belong_to(:project) }
- it { should have_many(:issues) }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:issues) }
end
describe "Mass assignment" do
@@ -26,8 +26,8 @@ describe Milestone do
describe "Validation" do
before { subject.stub(set_iid: false) }
- it { should validate_presence_of(:title) }
- it { should validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_presence_of(:project) }
end
let(:milestone) { create(:milestone) }
@@ -36,30 +36,30 @@ describe Milestone do
describe "#percent_complete" do
it "should not count open issues" do
milestone.issues << issue
- milestone.percent_complete.should == 0
+ expect(milestone.percent_complete).to eq(0)
end
it "should count closed issues" do
issue.close
milestone.issues << issue
- milestone.percent_complete.should == 100
+ expect(milestone.percent_complete).to eq(100)
end
it "should recover from dividing by zero" do
- milestone.issues.should_receive(:count).and_return(0)
- milestone.percent_complete.should == 100
+ expect(milestone.issues).to receive(:count).and_return(0)
+ expect(milestone.percent_complete).to eq(100)
end
end
describe "#expires_at" do
it "should be nil when due_date is unset" do
milestone.update_attributes(due_date: nil)
- milestone.expires_at.should be_nil
+ expect(milestone.expires_at).to be_nil
end
it "should not be nil when due_date is set" do
milestone.update_attributes(due_date: Date.tomorrow)
- milestone.expires_at.should be_present
+ expect(milestone.expires_at).to be_present
end
end
@@ -69,7 +69,7 @@ describe Milestone do
milestone.stub(due_date: Date.today.prev_year)
end
- it { milestone.expired?.should be_true }
+ it { expect(milestone.expired?).to be_truthy }
end
context "not expired" do
@@ -77,7 +77,7 @@ describe Milestone do
milestone.stub(due_date: Date.today.next_year)
end
- it { milestone.expired?.should be_false }
+ it { expect(milestone.expired?).to be_falsey }
end
end
@@ -89,7 +89,7 @@ describe Milestone do
)
end
- it { milestone.percent_complete.should == 75 }
+ it { expect(milestone.percent_complete).to eq(75) }
end
describe :items_count do
@@ -99,14 +99,14 @@ describe Milestone do
milestone.merge_requests << create(:merge_request)
end
- it { milestone.closed_items_count.should == 1 }
- it { milestone.open_items_count.should == 2 }
- it { milestone.total_items_count.should == 3 }
- it { milestone.is_empty?.should be_false }
+ it { expect(milestone.closed_items_count).to eq(1) }
+ it { expect(milestone.open_items_count).to eq(2) }
+ it { expect(milestone.total_items_count).to eq(3) }
+ it { expect(milestone.is_empty?).to be_falsey }
end
describe :can_be_closed? do
- it { milestone.can_be_closed?.should be_true }
+ it { expect(milestone.can_be_closed?).to be_truthy }
end
describe :is_empty? do
@@ -116,7 +116,7 @@ describe Milestone do
end
it 'Should return total count of issues and merge requests assigned to milestone' do
- milestone.total_items_count.should eq 2
+ expect(milestone.total_items_count).to eq 2
end
end
@@ -129,14 +129,14 @@ describe Milestone do
end
it 'should be true if milestone active and all nested issues closed' do
- milestone.can_be_closed?.should be_true
+ expect(milestone.can_be_closed?).to be_truthy
end
it 'should be false if milestone active and not all nested issues closed' do
issue.milestone = milestone
issue.save
- milestone.can_be_closed?.should be_false
+ expect(milestone.can_be_closed?).to be_falsey
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 3562ebed1ffe7bbf8f08b1492f0b9582dcbc4590..ed6845c82ccde162afc94e5cfc1d05551bd926d0 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -18,29 +18,29 @@ require 'spec_helper'
describe Namespace do
let!(:namespace) { create(:namespace) }
- it { should have_many :projects }
- it { should validate_presence_of :name }
- it { should validate_uniqueness_of(:name) }
- it { should validate_presence_of :path }
- it { should validate_uniqueness_of(:path) }
- it { should validate_presence_of :owner }
+ it { is_expected.to have_many :projects }
+ it { is_expected.to validate_presence_of :name }
+ it { is_expected.to validate_uniqueness_of(:name) }
+ it { is_expected.to validate_presence_of :path }
+ it { is_expected.to validate_uniqueness_of(:path) }
+ it { is_expected.to validate_presence_of :owner }
describe "Mass assignment" do
end
describe "Respond to" do
- it { should respond_to(:human_name) }
- it { should respond_to(:to_param) }
+ it { is_expected.to respond_to(:human_name) }
+ it { is_expected.to respond_to(:to_param) }
end
- it { Namespace.global_id.should == 'GLN' }
+ it { expect(Namespace.global_id).to eq('GLN') }
describe :to_param do
- it { namespace.to_param.should == namespace.path }
+ it { expect(namespace.to_param).to eq(namespace.path) }
end
describe :human_name do
- it { namespace.human_name.should == namespace.owner_name }
+ it { expect(namespace.human_name).to eq(namespace.owner_name) }
end
describe :search do
@@ -48,8 +48,8 @@ describe Namespace do
@namespace = create :namespace
end
- it { Namespace.search(@namespace.path).should == [@namespace] }
- it { Namespace.search('unknown').should == [] }
+ it { expect(Namespace.search(@namespace.path)).to eq([@namespace]) }
+ it { expect(Namespace.search('unknown')).to eq([]) }
end
describe :move_dir do
@@ -66,13 +66,23 @@ describe Namespace do
new_path = @namespace.path + "_new"
@namespace.stub(path_was: @namespace.path)
@namespace.stub(path: new_path)
- @namespace.move_dir.should be_true
+ expect(@namespace.move_dir).to be_truthy
end
end
describe :rm_dir do
it "should remove dir" do
- namespace.rm_dir.should be_true
+ expect(namespace.rm_dir).to be_truthy
end
end
+
+ describe :find_by_path_or_name do
+ before do
+ @namespace = create(:namespace, name: 'WoW', path: 'woW')
+ end
+
+ it { expect(Namespace.find_by_path_or_name('wow')).to eq(@namespace) }
+ it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) }
+ it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) }
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 6ab7162c15c5b470036171f7fe3784a2446206e6..a7bf5081d5b8a9c98176301132c712ab8e5823de 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -21,17 +21,17 @@ require 'spec_helper'
describe Note do
describe "Associations" do
- it { should belong_to(:project) }
- it { should belong_to(:noteable) }
- it { should belong_to(:author).class_name('User') }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:noteable) }
+ it { is_expected.to belong_to(:author).class_name('User') }
end
describe "Mass assignment" do
end
describe "Validation" do
- it { should validate_presence_of(:note) }
- it { should validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:note) }
+ it { is_expected.to validate_presence_of(:project) }
end
describe "Voting score" do
@@ -39,44 +39,44 @@ describe Note do
it "recognizes a neutral note" do
note = create(:votable_note, note: "This is not a +1 note")
- note.should_not be_upvote
- note.should_not be_downvote
+ expect(note).not_to be_upvote
+ expect(note).not_to be_downvote
end
it "recognizes a neutral emoji note" do
note = build(:votable_note, note: "I would :+1: this, but I don't want to")
- note.should_not be_upvote
- note.should_not be_downvote
+ expect(note).not_to be_upvote
+ expect(note).not_to be_downvote
end
it "recognizes a +1 note" do
note = create(:votable_note, note: "+1 for this")
- note.should be_upvote
+ expect(note).to be_upvote
end
it "recognizes a +1 emoji as a vote" do
note = build(:votable_note, note: ":+1: for this")
- note.should be_upvote
+ expect(note).to be_upvote
end
it "recognizes a thumbsup emoji as a vote" do
note = build(:votable_note, note: ":thumbsup: for this")
- note.should be_upvote
+ expect(note).to be_upvote
end
it "recognizes a -1 note" do
note = create(:votable_note, note: "-1 for this")
- note.should be_downvote
+ expect(note).to be_downvote
end
it "recognizes a -1 emoji as a vote" do
note = build(:votable_note, note: ":-1: for this")
- note.should be_downvote
+ expect(note).to be_downvote
end
it "recognizes a thumbsdown emoji as a vote" do
note = build(:votable_note, note: ":thumbsdown: for this")
- note.should be_downvote
+ expect(note).to be_downvote
end
end
@@ -87,22 +87,22 @@ describe Note do
let!(:commit) { note.noteable }
it "should be accessible through #noteable" do
- note.commit_id.should == commit.id
- note.noteable.should be_a(Commit)
- note.noteable.should == commit
+ expect(note.commit_id).to eq(commit.id)
+ expect(note.noteable).to be_a(Commit)
+ expect(note.noteable).to eq(commit)
end
it "should save a valid note" do
- note.commit_id.should == commit.id
+ expect(note.commit_id).to eq(commit.id)
note.noteable == commit
end
it "should be recognized by #for_commit?" do
- note.should be_for_commit
+ expect(note).to be_for_commit
end
it "should not be votable" do
- note.should_not be_votable
+ expect(note).not_to be_votable
end
end
@@ -111,20 +111,20 @@ describe Note do
let!(:commit) { note.noteable }
it "should save a valid note" do
- note.commit_id.should == commit.id
- note.noteable.id.should == commit.id
+ expect(note.commit_id).to eq(commit.id)
+ expect(note.noteable.id).to eq(commit.id)
end
it "should be recognized by #for_diff_line?" do
- note.should be_for_diff_line
+ expect(note).to be_for_diff_line
end
it "should be recognized by #for_commit_diff_line?" do
- note.should be_for_commit_diff_line
+ expect(note).to be_for_commit_diff_line
end
it "should not be votable" do
- note.should_not be_votable
+ expect(note).not_to be_votable
end
end
@@ -132,7 +132,7 @@ describe Note do
let!(:note) { create(:note_on_issue, note: "+1 from me") }
it "should not be votable" do
- note.should be_votable
+ expect(note).to be_votable
end
end
@@ -140,7 +140,7 @@ describe Note do
let!(:note) { create(:note_on_merge_request, note: "+1 from me") }
it "should be votable" do
- note.should be_votable
+ expect(note).to be_votable
end
end
@@ -148,7 +148,7 @@ describe Note do
let!(:note) { create(:note_on_merge_request_diff, note: "+1 from me") }
it "should not be votable" do
- note.should_not be_votable
+ expect(note).not_to be_votable
end
end
@@ -161,20 +161,35 @@ describe Note do
subject { Note.create_status_change_note(thing, project, author, status, nil) }
it 'creates and saves a Note' do
- should be_a Note
- subject.id.should_not be_nil
+ is_expected.to be_a Note
+ expect(subject.id).not_to be_nil
end
- its(:noteable) { should == thing }
- its(:project) { should == thing.project }
- its(:author) { should == author }
- its(:note) { should =~ /Status changed to #{status}/ }
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(thing) }
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(thing.project) }
+ end
+
+ describe '#author' do
+ subject { super().author }
+ it { is_expected.to eq(author) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("Status changed to #{status}") }
+ end
it 'appends a back-reference if a closing mentionable is supplied' do
commit = double('commit', gfm_reference: 'commit 123456')
n = Note.create_status_change_note(thing, project, author, status, commit)
- n.note.should =~ /Status changed to #{status} by commit 123456/
+ expect(n.note).to eq("Status changed to #{status} by commit 123456")
end
end
@@ -182,24 +197,130 @@ describe Note do
let(:project) { create(:project) }
let(:thing) { create(:issue, project: project) }
let(:author) { create(:user) }
- let(:assignee) { create(:user) }
+ let(:assignee) { create(:user, username: "assigned_user") }
subject { Note.create_assignee_change_note(thing, project, author, assignee) }
context 'creates and saves a Note' do
- it { should be_a Note }
- its(:id) { should_not be_nil }
+ it { is_expected.to be_a Note }
+
+ describe '#id' do
+ subject { super().id }
+ it { is_expected.not_to be_nil }
+ end
+ end
+
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(thing) }
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(thing.project) }
+ end
+
+ describe '#author' do
+ subject { super().author }
+ it { is_expected.to eq(author) }
end
- its(:noteable) { should == thing }
- its(:project) { should == thing.project }
- its(:author) { should == author }
- its(:note) { should =~ /Reassigned to @#{assignee.username}/ }
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq('Reassigned to @assigned_user') }
+ end
context 'assignee is removed' do
let(:assignee) { nil }
- its(:note) { should =~ /Assignee removed/ }
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq('Assignee removed') }
+ end
+ end
+ end
+
+ describe '#create_labels_change_note' do
+ let(:project) { create(:project) }
+ let(:thing) { create(:issue, project: project) }
+ let(:author) { create(:user) }
+ let(:label1) { create(:label) }
+ let(:label2) { create(:label) }
+ let(:added_labels) { [label1, label2] }
+ let(:removed_labels) { [] }
+
+ subject { Note.create_labels_change_note(thing, project, author, added_labels, removed_labels) }
+
+ context 'creates and saves a Note' do
+ it { is_expected.to be_a Note }
+
+ describe '#id' do
+ subject { super().id }
+ it { is_expected.not_to be_nil }
+ end
+ end
+
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(thing) }
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(thing.project) }
+ end
+
+ describe '#author' do
+ subject { super().author }
+ it { is_expected.to eq(author) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("Added ~#{label1.id} ~#{label2.id} labels") }
+ end
+
+ context 'label is removed' do
+ let(:added_labels) { [label1] }
+ let(:removed_labels) { [label2] }
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("Added ~#{label1.id} and removed ~#{label2.id} labels") }
+ end
+ end
+ end
+
+ describe '#create_milestone_change_note' do
+ let(:project) { create(:project) }
+ let(:thing) { create(:issue, project: project) }
+ let(:milestone) { create(:milestone, project: project, title: "first_milestone") }
+ let(:author) { create(:user) }
+
+ subject { Note.create_milestone_change_note(thing, project, author, milestone) }
+
+ context 'creates and saves a Note' do
+ it { is_expected.to be_a Note }
+
+ describe '#id' do
+ subject { super().id }
+ it { is_expected.not_to be_nil }
+ end
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(thing.project) }
+ end
+
+ describe '#author' do
+ subject { super().author }
+ it { is_expected.to eq(author) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("Milestone changed to first_milestone") }
end
end
@@ -216,64 +337,144 @@ describe Note do
context 'issue from a merge request' do
subject { Note.create_cross_reference_note(issue, mergereq, author, project) }
- it { should be_valid }
- its(:noteable) { should == issue }
- its(:project) { should == issue.project }
- its(:author) { should == author }
- its(:note) { should == "_mentioned in merge request !#{mergereq.iid}_" }
+ it { is_expected.to be_valid }
+
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(issue) }
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(issue.project) }
+ end
+
+ describe '#author' do
+ subject { super().author }
+ it { is_expected.to eq(author) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("mentioned in merge request !#{mergereq.iid}") }
+ end
end
context 'issue from a commit' do
subject { Note.create_cross_reference_note(issue, commit, author, project) }
- it { should be_valid }
- its(:noteable) { should == issue }
- its(:note) { should == "_mentioned in commit #{commit.sha}_" }
+ it { is_expected.to be_valid }
+
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(issue) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("mentioned in commit #{commit.sha}") }
+ end
end
context 'merge request from an issue' do
subject { Note.create_cross_reference_note(mergereq, issue, author, project) }
- it { should be_valid }
- its(:noteable) { should == mergereq }
- its(:project) { should == mergereq.project }
- its(:note) { should == "_mentioned in issue ##{issue.iid}_" }
+ it { is_expected.to be_valid }
+
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(mergereq) }
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(mergereq.project) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("mentioned in issue ##{issue.iid}") }
+ end
end
context 'commit from a merge request' do
subject { Note.create_cross_reference_note(commit, mergereq, author, project) }
- it { should be_valid }
- its(:noteable) { should == commit }
- its(:project) { should == project }
- its(:note) { should == "_mentioned in merge request !#{mergereq.iid}_" }
+ it { is_expected.to be_valid }
+
+ describe '#noteable' do
+ subject { super().noteable }
+ it { is_expected.to eq(commit) }
+ end
+
+ describe '#project' do
+ subject { super().project }
+ it { is_expected.to eq(project) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("mentioned in merge request !#{mergereq.iid}") }
+ end
end
context 'commit contained in a merge request' do
subject { Note.create_cross_reference_note(mergereq.commits.first, mergereq, author, project) }
- it { should be_nil }
+ it { is_expected.to be_nil }
end
context 'commit from issue' do
subject { Note.create_cross_reference_note(commit, issue, author, project) }
- it { should be_valid }
- its(:noteable_type) { should == "Commit" }
- its(:noteable_id) { should be_nil }
- its(:commit_id) { should == commit.id }
- its(:note) { should == "_mentioned in issue ##{issue.iid}_" }
+ it { is_expected.to be_valid }
+
+ describe '#noteable_type' do
+ subject { super().noteable_type }
+ it { is_expected.to eq("Commit") }
+ end
+
+ describe '#noteable_id' do
+ subject { super().noteable_id }
+ it { is_expected.to be_nil }
+ end
+
+ describe '#commit_id' do
+ subject { super().commit_id }
+ it { is_expected.to eq(commit.id) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("mentioned in issue ##{issue.iid}") }
+ end
end
context 'commit from commit' do
let(:parent_commit) { commit.parents.first }
subject { Note.create_cross_reference_note(commit, parent_commit, author, project) }
- it { should be_valid }
- its(:noteable_type) { should == "Commit" }
- its(:noteable_id) { should be_nil }
- its(:commit_id) { should == commit.id }
- its(:note) { should == "_mentioned in commit #{parent_commit.id}_" }
+ it { is_expected.to be_valid }
+
+ describe '#noteable_type' do
+ subject { super().noteable_type }
+ it { is_expected.to eq("Commit") }
+ end
+
+ describe '#noteable_id' do
+ subject { super().noteable_id }
+ it { is_expected.to be_nil }
+ end
+
+ describe '#commit_id' do
+ subject { super().commit_id }
+ it { is_expected.to eq(commit.id) }
+ end
+
+ describe '#note' do
+ subject { super().note }
+ it { is_expected.to eq("mentioned in commit #{parent_commit.id}") }
+ end
end
end
@@ -289,11 +490,11 @@ describe Note do
end
it 'detects if a mentionable has already been mentioned' do
- Note.cross_reference_exists?(issue, commit0).should be_true
+ expect(Note.cross_reference_exists?(issue, commit0)).to be_truthy
end
it 'detects if a mentionable has not already been mentioned' do
- Note.cross_reference_exists?(issue, commit1).should be_false
+ expect(Note.cross_reference_exists?(issue, commit1)).to be_falsey
end
context 'commit on commit' do
@@ -301,8 +502,43 @@ describe Note do
Note.create_cross_reference_note(commit0, commit1, author, project)
end
- it { Note.cross_reference_exists?(commit0, commit1).should be_true }
- it { Note.cross_reference_exists?(commit1, commit0).should be_false }
+ it { expect(Note.cross_reference_exists?(commit0, commit1)).to be_truthy }
+ it { expect(Note.cross_reference_exists?(commit1, commit0)).to be_falsey }
+ end
+
+ context 'legacy note with Markdown emphasis' do
+ let(:issue2) { create :issue, project: project }
+ let!(:note) do
+ create :note, system: true, noteable_id: issue2.id,
+ noteable_type: "Issue", note: "_mentioned in issue " \
+ "#{issue.project.path_with_namespace}##{issue.iid}_"
+ end
+
+ it 'detects if a mentionable with emphasis has been mentioned' do
+ expect(Note.cross_reference_exists?(issue2, issue)).to be_truthy
+ end
+ end
+ end
+
+ describe '#cross_references_with_underscores?' do
+ let(:project) { create :project, path: "first_project" }
+ let(:second_project) { create :project, path: "second_project" }
+
+ let(:author) { create :user }
+ let(:issue0) { create :issue, project: project }
+ let(:issue1) { create :issue, project: second_project }
+ let!(:note) { Note.create_cross_reference_note(issue0, issue1, author, project) }
+
+ it 'detects if a mentionable has already been mentioned' do
+ expect(Note.cross_reference_exists?(issue0, issue1)).to be_truthy
+ end
+
+ it 'detects if a mentionable has not already been mentioned' do
+ expect(Note.cross_reference_exists?(issue1, issue0)).to be_falsey
+ end
+
+ it 'detects that text has underscores' do
+ expect(note.note).to eq("mentioned in issue #{second_project.path_with_namespace}##{issue1.iid}")
end
end
@@ -312,25 +548,37 @@ describe Note do
let(:other) { create(:issue, project: project) }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
+ let(:label) { create(:label) }
+ let(:milestone) { create(:milestone) }
it 'should recognize user-supplied notes as non-system' do
@note = create(:note_on_issue)
- @note.should_not be_system
+ expect(@note).not_to be_system
end
it 'should identify status-change notes as system notes' do
@note = Note.create_status_change_note(issue, project, author, 'closed', nil)
- @note.should be_system
+ expect(@note).to be_system
end
it 'should identify cross-reference notes as system notes' do
@note = Note.create_cross_reference_note(issue, other, author, project)
- @note.should be_system
+ expect(@note).to be_system
end
it 'should identify assignee-change notes as system notes' do
@note = Note.create_assignee_change_note(issue, project, author, assignee)
- @note.should be_system
+ expect(@note).to be_system
+ end
+
+ it 'should identify label-change notes as system notes' do
+ @note = Note.create_labels_change_note(issue, project, author, [label], [])
+ expect(@note).to be_system
+ end
+
+ it 'should identify milestone-change notes as system notes' do
+ @note = Note.create_milestone_change_note(issue, project, author, milestone)
+ expect(@note).to be_system
end
end
@@ -351,9 +599,9 @@ describe Note do
@p2.project_members.create(user: @u3, access_level: ProjectMember::GUEST)
end
- it { @abilities.allowed?(@u1, :read_note, @p1).should be_false }
- it { @abilities.allowed?(@u2, :read_note, @p1).should be_true }
- it { @abilities.allowed?(@u3, :read_note, @p1).should be_false }
+ it { expect(@abilities.allowed?(@u1, :read_note, @p1)).to be_falsey }
+ it { expect(@abilities.allowed?(@u2, :read_note, @p1)).to be_truthy }
+ it { expect(@abilities.allowed?(@u3, :read_note, @p1)).to be_falsey }
end
describe :write do
@@ -362,9 +610,9 @@ describe Note do
@p2.project_members.create(user: @u3, access_level: ProjectMember::DEVELOPER)
end
- it { @abilities.allowed?(@u1, :write_note, @p1).should be_false }
- it { @abilities.allowed?(@u2, :write_note, @p1).should be_true }
- it { @abilities.allowed?(@u3, :write_note, @p1).should be_false }
+ it { expect(@abilities.allowed?(@u1, :write_note, @p1)).to be_falsey }
+ it { expect(@abilities.allowed?(@u2, :write_note, @p1)).to be_truthy }
+ it { expect(@abilities.allowed?(@u3, :write_note, @p1)).to be_falsey }
end
describe :admin do
@@ -374,9 +622,9 @@ describe Note do
@p2.project_members.create(user: @u3, access_level: ProjectMember::MASTER)
end
- it { @abilities.allowed?(@u1, :admin_note, @p1).should be_false }
- it { @abilities.allowed?(@u2, :admin_note, @p1).should be_true }
- it { @abilities.allowed?(@u3, :admin_note, @p1).should be_false }
+ it { expect(@abilities.allowed?(@u1, :admin_note, @p1)).to be_falsey }
+ it { expect(@abilities.allowed?(@u2, :admin_note, @p1)).to be_truthy }
+ it { expect(@abilities.allowed?(@u3, :admin_note, @p1)).to be_falsey }
end
end
diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb
index 5c8d1e7438b5fcce0a2ce4fa3a10b9c4f33dcdf7..1ee1900354378f7f4141a257043946ce4e02305a 100644
--- a/spec/models/project_security_spec.rb
+++ b/spec/models/project_security_spec.rb
@@ -23,7 +23,7 @@ describe Project do
describe "Non member rules" do
it "should deny for non-project users any actions" do
admin_actions.each do |action|
- @abilities.allowed?(@u1, action, @p1).should be_false
+ expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey
end
end
end
@@ -35,7 +35,7 @@ describe Project do
it "should allow for project user any guest actions" do
guest_actions.each do |action|
- @abilities.allowed?(@u2, action, @p1).should be_true
+ expect(@abilities.allowed?(@u2, action, @p1)).to be_truthy
end
end
end
@@ -47,7 +47,7 @@ describe Project do
it "should allow for project user any report actions" do
report_actions.each do |action|
- @abilities.allowed?(@u2, action, @p1).should be_true
+ expect(@abilities.allowed?(@u2, action, @p1)).to be_truthy
end
end
end
@@ -60,13 +60,13 @@ describe Project do
it "should deny for developer master-specific actions" do
[dev_actions - report_actions].each do |action|
- @abilities.allowed?(@u2, action, @p1).should be_false
+ expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end
end
it "should allow for project user any dev actions" do
dev_actions.each do |action|
- @abilities.allowed?(@u3, action, @p1).should be_true
+ expect(@abilities.allowed?(@u3, action, @p1)).to be_truthy
end
end
end
@@ -79,13 +79,13 @@ describe Project do
it "should deny for developer master-specific actions" do
[master_actions - dev_actions].each do |action|
- @abilities.allowed?(@u2, action, @p1).should be_false
+ expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end
end
it "should allow for project user any master actions" do
master_actions.each do |action|
- @abilities.allowed?(@u3, action, @p1).should be_true
+ expect(@abilities.allowed?(@u3, action, @p1)).to be_truthy
end
end
end
@@ -98,13 +98,13 @@ describe Project do
it "should deny for masters admin-specific actions" do
[admin_actions - master_actions].each do |action|
- @abilities.allowed?(@u2, action, @p1).should be_false
+ expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end
end
it "should allow for project owner any admin actions" do
admin_actions.each do |action|
- @abilities.allowed?(@u4, action, @p1).should be_true
+ expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy
end
end
end
diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..13c8d54a2af522664582b7ce487b50a9a84e5235
--- /dev/null
+++ b/spec/models/project_services/asana_service_spec.rb
@@ -0,0 +1,65 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe AsanaService, models: true do
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'Validations' do
+ context 'active' do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :api_key }
+ end
+ end
+
+ describe 'Execute' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ @asana = AsanaService.new
+ @asana.stub(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ api_key: 'verySecret',
+ restrict_to_branch: 'master'
+ )
+ end
+
+ it 'should call Asana service to created a story' do
+ expect(Asana::Task).to receive(:find).with('123456').once
+
+ @asana.check_commit('related to #123456', 'pushed')
+ end
+
+ it 'should call Asana service to created a story and close a task' do
+ expect(Asana::Task).to receive(:find).with('456789').twice
+
+ @asana.check_commit('fix #456789', 'pushed')
+ end
+ end
+end
diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..91730da1eecdbdf7d6ec85e8dca5d8a14eb649b9
--- /dev/null
+++ b/spec/models/project_services/assembla_service_spec.rb
@@ -0,0 +1,53 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe AssemblaService, models: true do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ @assembla_service = AssemblaService.new
+ @assembla_service.stub(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ token: 'verySecret',
+ subdomain: 'project_name'
+ )
+ @sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
+ @api_url = 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret'
+ WebMock.stub_request(:post, @api_url)
+ end
+
+ it "should call Assembla API" do
+ @assembla_service.execute(@sample_data)
+ expect(WebMock).to have_requested(:post, @api_url).with(
+ body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
+ ).once
+ end
+ end
+end
diff --git a/spec/models/project_services/buildbox_service_spec.rb b/spec/models/project_services/buildbox_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9f29fbe12b080df9d98b9234ad9706f3cfb219ca
--- /dev/null
+++ b/spec/models/project_services/buildbox_service_spec.rb
@@ -0,0 +1,82 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe BuildboxService do
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'commits methods' do
+ before do
+ @project = Project.new
+ @project.stub(
+ default_branch: 'default-brancho'
+ )
+
+ @service = BuildboxService.new
+ @service.stub(
+ project: @project,
+ service_hook: true,
+ project_url: 'https://buildkite.com/account-name/example-project',
+ token: 'secret-sauce-webhook-token:secret-sauce-status-token'
+ )
+ end
+
+ describe :webhook_url do
+ it 'returns the webhook url' do
+ expect(@service.webhook_url).to eq(
+ 'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token'
+ )
+ end
+ end
+
+ describe :commit_status_path do
+ it 'returns the correct status page' do
+ expect(@service.commit_status_path('2ab7834c')).to eq(
+ 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=2ab7834c'
+ )
+ end
+ end
+
+ describe :build_page do
+ it 'returns the correct build page' do
+ expect(@service.build_page('2ab7834c', nil)).to eq(
+ 'https://buildkite.com/account-name/example-project/builds?commit=2ab7834c'
+ )
+ end
+ end
+
+ describe :builds_page do
+ it 'returns the correct path to the builds page' do
+ expect(@service.builds_path).to eq(
+ 'https://buildkite.com/account-name/example-project/builds?branch=default-brancho'
+ )
+ end
+ end
+
+ describe :status_img_path do
+ it 'returns the correct path to the status image' do
+ expect(@service.status_img_path).to eq('https://badge.buildkite.com/secret-sauce-status-token.svg')
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..73f68301a34e309a8ff7b76b587875964c6005bb
--- /dev/null
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -0,0 +1,52 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe FlowdockService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ @flowdock_service = FlowdockService.new
+ @flowdock_service.stub(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ token: 'verySecret'
+ )
+ @sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
+ @api_url = 'https://api.flowdock.com/v1/git/verySecret'
+ WebMock.stub_request(:post, @api_url)
+ end
+
+ it "should call FlowDock API" do
+ @flowdock_service.execute(@sample_data)
+ expect(WebMock).to have_requested(:post, @api_url).with(
+ body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
+ ).once
+ end
+ end
+end
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d44064bbe6a14f839eb857b9fe9f495b3a3d788c
--- /dev/null
+++ b/spec/models/project_services/gemnasium_service_spec.rb
@@ -0,0 +1,48 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe GemnasiumService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ @gemnasium_service = GemnasiumService.new
+ @gemnasium_service.stub(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ token: 'verySecret',
+ api_key: 'GemnasiumUserApiKey'
+ )
+ @sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
+ end
+ it "should call Gemnasium service" do
+ expect(Gemnasium::GitlabService).to receive(:execute).with(an_instance_of(Hash)).once
+ @gemnasium_service.execute(@sample_data)
+ end
+ end
+end
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..610f33c5823c0e93cab8df474f36f0d1aaba4fec
--- /dev/null
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -0,0 +1,49 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe GitlabCiService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Mass assignment" do
+ end
+
+ describe 'commits methods' do
+ before do
+ @service = GitlabCiService.new
+ @service.stub(
+ service_hook: true,
+ project_url: 'http://ci.gitlab.org/projects/2',
+ token: 'verySecret'
+ )
+ end
+
+ describe :commit_status_path do
+ it { expect(@service.commit_status_path("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c/status.json?token=verySecret")}
+ end
+
+ describe :build_page do
+ it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c")}
+ end
+ end
+end
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..959044dc727561702d68765f1037f6a40604dc39
--- /dev/null
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -0,0 +1,65 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe GitlabIssueTrackerService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+
+ describe 'project and issue urls' do
+ let(:project) { create(:project) }
+
+ context 'with absolute urls' do
+ before do
+ @service = project.create_gitlab_issue_tracker_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should give the correct path' do
+ expect(@service.project_url).to eq("/#{project.path_with_namespace}/issues")
+ expect(@service.new_issue_url).to eq("/#{project.path_with_namespace}/issues/new")
+ expect(@service.issue_url(432)).to eq("/#{project.path_with_namespace}/issues/432")
+ end
+ end
+
+ context 'with enabled relative urls' do
+ before do
+ Settings.gitlab.stub(:relative_url_root).and_return("/gitlab/root")
+ @service = project.create_gitlab_issue_tracker_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should give the correct path' do
+ expect(@service.project_url).to eq("/gitlab/root/#{project.path_with_namespace}/issues")
+ expect(@service.new_issue_url).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new")
+ expect(@service.issue_url(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432")
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8ab847e64323f06c127ba3ecf8949590d86caff4
--- /dev/null
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -0,0 +1,217 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe HipchatService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:hipchat) { HipchatService.new }
+ let(:user) { create(:user, username: 'username') }
+ let(:project) { create(:project, name: 'project') }
+ let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' }
+ let(:project_name) { project.name_with_namespace.gsub(/\s/, '') }
+
+ before(:each) do
+ hipchat.stub(
+ project_id: project.id,
+ project: project,
+ room: 123456,
+ server: 'https://hipchat.example.com',
+ token: 'verySecret'
+ )
+ WebMock.stub_request(:post, api_url)
+ end
+
+ context 'push events' do
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ it "should call Hipchat API for push events" do
+ hipchat.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create a push message" do
+ message = hipchat.send(:create_push_message, push_sample_data)
+
+ obj_attr = push_sample_data[:object_attributes]
+ branch = push_sample_data[:ref].gsub('refs/heads/', '')
+ expect(message).to include("#{user.name} pushed to branch " \
+ "#{branch} of " \
+ "#{project_name}")
+ end
+ end
+
+ context 'tag_push events' do
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build(project, user, Gitlab::Git::BLANK_SHA, '1' * 40, 'refs/tags/test', []) }
+
+ it "should call Hipchat API for tag push events" do
+ hipchat.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create a tag push message" do
+ message = hipchat.send(:create_push_message, push_sample_data)
+
+ obj_attr = push_sample_data[:object_attributes]
+ expect(message).to eq("#{user.name} pushed new tag " \
+ "test to " \
+ "#{project_name}\n")
+ end
+ end
+
+ context 'issue events' do
+ let(:issue) { create(:issue, title: 'Awesome issue', description: 'please fix') }
+ let(:issue_service) { Issues::CreateService.new(project, user) }
+ let(:issues_sample_data) { issue_service.hook_data(issue, 'open') }
+
+ it "should call Hipchat API for issue events" do
+ hipchat.execute(issues_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create an issue message" do
+ message = hipchat.send(:create_issue_message, issues_sample_data)
+
+ obj_attr = issues_sample_data[:object_attributes]
+ expect(message).to eq("#{user.name} opened " \
+ "issue ##{obj_attr["iid"]} in " \
+ "#{project_name}: " \
+ "Awesome issue" \
+ "please fix
")
+ end
+ end
+
+ context 'merge request events' do
+ let(:merge_request) { create(:merge_request, description: 'please fix', title: 'Awesome merge request', target_project: project, source_project: project) }
+ let(:merge_service) { MergeRequests::CreateService.new(project, user) }
+ let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') }
+
+ it "should call Hipchat API for merge requests events" do
+ hipchat.execute(merge_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create a merge request message" do
+ message = hipchat.send(:create_merge_request_message,
+ merge_sample_data)
+
+ obj_attr = merge_sample_data[:object_attributes]
+ expect(message).to eq("#{user.name} opened " \
+ "merge request ##{obj_attr["iid"]} in " \
+ "#{project_name}: " \
+ "Awesome merge request" \
+ "please fix
")
+ end
+ end
+
+ context "Note events" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:snippet) { create(:project_snippet, project: project) }
+ let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
+ let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") }
+ let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")}
+ let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") }
+
+ it "should call Hipchat API for commit comment events" do
+ data = Gitlab::NoteDataBuilder.build(commit_note, user)
+ hipchat.execute(data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ commit_id = Commit.truncate_sha(data[:commit][:id])
+ title = hipchat.send(:format_title, data[:commit][:message])
+
+ expect(message).to eq("#{user.name} commented on " \
+ "commit #{commit_id} in " \
+ "#{project_name}: " \
+ "#{title}" \
+ "a comment on a commit
")
+ end
+
+ it "should call Hipchat API for merge request comment events" do
+ data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
+ hipchat.execute(data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ merge_id = data[:merge_request]['iid']
+ title = data[:merge_request]['title']
+
+ expect(message).to eq("#{user.name} commented on " \
+ "merge request ##{merge_id} in " \
+ "#{project_name}: " \
+ "#{title}" \
+ "merge request note
")
+ end
+
+ it "should call Hipchat API for issue comment events" do
+ data = Gitlab::NoteDataBuilder.build(issue_note, user)
+ hipchat.execute(data)
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ issue_id = data[:issue]['iid']
+ title = data[:issue]['title']
+
+ expect(message).to eq("#{user.name} commented on " \
+ "issue ##{issue_id} in " \
+ "#{project_name}: " \
+ "#{title}" \
+ "issue note
")
+ end
+
+ it "should call Hipchat API for snippet comment events" do
+ data = Gitlab::NoteDataBuilder.build(snippet_note, user)
+ hipchat.execute(data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ snippet_id = data[:snippet]['id']
+ title = data[:snippet]['title']
+
+ expect(message).to eq("#{user.name} commented on " \
+ "snippet ##{snippet_id} in " \
+ "#{project_name}: " \
+ "#{title}" \
+ "snippet note
")
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d55399bc360102ea6037502ce6adba53013dea4e
--- /dev/null
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -0,0 +1,108 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+require 'socket'
+require 'json'
+
+describe IrkerService do
+ describe 'Associations' do
+ it { should belong_to :project }
+ it { should have_one :service_hook }
+ end
+
+ describe 'Validations' do
+ before do
+ subject.active = true
+ subject.properties['recipients'] = _recipients
+ end
+
+ context 'active' do
+ let(:_recipients) { nil }
+ it { should validate_presence_of :recipients }
+ end
+
+ context 'too many recipients' do
+ let(:_recipients) { 'a b c d' }
+ it 'should add an error if there is too many recipients' do
+ subject.send :check_recipients_count
+ subject.errors.should_not be_blank
+ end
+ end
+
+ context '3 recipients' do
+ let(:_recipients) { 'a b c' }
+ it 'should not add an error if there is 3 recipients' do
+ subject.send :check_recipients_count
+ subject.errors.should be_blank
+ end
+ end
+ end
+
+ describe 'Execute' do
+ let(:irker) { IrkerService.new }
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ let(:recipients) { '#commits' }
+ let(:colorize_messages) { '1' }
+
+ before do
+ irker.stub(
+ active: true,
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ properties: {
+ 'recipients' => recipients,
+ 'colorize_messages' => colorize_messages
+ }
+ )
+ irker.settings = {
+ server_ip: 'localhost',
+ server_port: 6659,
+ max_channels: 3,
+ default_irc_uri: 'irc://chat.freenode.net/'
+ }
+ irker.valid?
+ @irker_server = TCPServer.new 'localhost', 6659
+ end
+
+ after do
+ @irker_server.close
+ end
+
+ it 'should send valid JSON messages to an Irker listener' do
+ irker.execute(sample_data)
+
+ conn = @irker_server.accept
+ conn.readlines.each do |line|
+ msg = JSON.load(line.chomp("\n"))
+ msg.keys.should match_array(['to', 'privmsg'])
+ if msg['to'].is_a?(String)
+ msg['to'].should == 'irc://chat.freenode.net/#commits'
+ else
+ msg['to'].should match_array(['irc://chat.freenode.net/#commits'])
+ end
+ end
+ conn.close
+ end
+ end
+end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..355911e637771a8227faac071953896ec239e5a5
--- /dev/null
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -0,0 +1,102 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe JiraService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :project_url }
+ it { is_expected.to validate_presence_of :issues_url }
+ it { is_expected.to validate_presence_of :new_issue_url }
+ end
+ end
+
+ describe 'description and title' do
+ let(:project) { create(:project) }
+
+ context 'when it is not set' do
+ before do
+ @service = project.create_jira_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should be initialized' do
+ expect(@service.title).to eq('JIRA')
+ expect(@service.description).to eq("Jira issue tracker")
+ end
+ end
+
+ context 'when it is set' do
+ before do
+ properties = { 'title' => 'Jira One', 'description' => 'Jira One issue tracker' }
+ @service = project.create_jira_service(active: true, properties: properties)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it "should be correct" do
+ expect(@service.title).to eq('Jira One')
+ expect(@service.description).to eq('Jira One issue tracker')
+ end
+ end
+ end
+
+ describe 'project and issue urls' do
+ let(:project) { create(:project) }
+
+ context 'when gitlab.yml was initialized' do
+ before do
+ settings = { "jira" => {
+ "title" => "Jira",
+ "project_url" => "http://jira.sample/projects/project_a",
+ "issues_url" => "http://jira.sample/issues/:id",
+ "new_issue_url" => "http://jira.sample/projects/project_a/issues/new"
+ }
+ }
+ allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
+ @service = project.create_jira_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should be prepopulated with the settings' do
+ expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a')
+ expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id")
+ expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new")
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5a18fd09bfc95caf51c590106bb2ee9effbe647c
--- /dev/null
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -0,0 +1,74 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe PushoverService do
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'Validations' do
+ context 'active' do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :api_key }
+ it { is_expected.to validate_presence_of :user_key }
+ it { is_expected.to validate_presence_of :priority }
+ end
+ end
+
+ describe 'Execute' do
+ let(:pushover) { PushoverService.new }
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ let(:api_key) { 'verySecret' }
+ let(:user_key) { 'verySecret' }
+ let(:device) { 'myDevice' }
+ let(:priority) { 0 }
+ let(:sound) { 'bike' }
+ let(:api_url) { 'https://api.pushover.net/1/messages.json' }
+
+ before do
+ pushover.stub(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ api_key: api_key,
+ user_key: user_key,
+ device: device,
+ priority: priority,
+ sound: sound
+ )
+
+ WebMock.stub_request(:post, api_url)
+ end
+
+ it 'should call Pushover API' do
+ pushover.execute(sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+ end
+end
diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8bca1fef44c50f1c281ad2c405fee18e8db6b2cb
--- /dev/null
+++ b/spec/models/project_services/slack_service/issue_message_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe SlackService::IssueMessage do
+ subject { SlackService::IssueMessage.new(args) }
+
+ let(:args) {
+ {
+ user: {
+ name: 'Test User',
+ username: 'Test User'
+ },
+ project_name: 'project_name',
+ project_url: 'somewhere.com',
+
+ object_attributes: {
+ title: 'Issue title',
+ id: 10,
+ iid: 100,
+ assignee_id: 1,
+ url: 'url',
+ action: 'open',
+ state: 'opened',
+ description: 'issue description'
+ }
+ }
+ }
+
+ let(:color) { '#345' }
+
+ context 'open' do
+ it 'returns a message regarding opening of issues' do
+ expect(subject.pretext).to eq(
+ 'Test User opened in : '\
+ '*Issue title*')
+ expect(subject.attachments).to eq([
+ {
+ text: "issue description",
+ color: color,
+ }
+ ])
+ end
+ end
+
+ context 'close' do
+ before do
+ args[:object_attributes][:action] = 'close'
+ args[:object_attributes][:state] = 'closed'
+ end
+ it 'returns a message regarding closing of issues' do
+ expect(subject.pretext). to eq(
+ 'Test User closed in : '\
+ '*Issue title*')
+ expect(subject.attachments).to be_empty
+ end
+ end
+end
diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aeb408aa766d4b164f7a6daaad07c83f25f5664e
--- /dev/null
+++ b/spec/models/project_services/slack_service/merge_message_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe SlackService::MergeMessage do
+ subject { SlackService::MergeMessage.new(args) }
+
+ let(:args) {
+ {
+ user: {
+ name: 'Test User',
+ username: 'Test User'
+ },
+ project_name: 'project_name',
+ project_url: 'somewhere.com',
+
+ object_attributes: {
+ title: "Issue title\nSecond line",
+ id: 10,
+ iid: 100,
+ assignee_id: 1,
+ url: 'url',
+ state: 'opened',
+ description: 'issue description',
+ source_branch: 'source_branch',
+ target_branch: 'target_branch',
+ }
+ }
+ }
+
+ let(:color) { '#345' }
+
+ context 'open' do
+ it 'returns a message regarding opening of merge requests' do
+ expect(subject.pretext).to eq(
+ 'Test User opened '\
+ 'in : *Issue title*')
+ expect(subject.attachments).to be_empty
+ end
+ end
+
+ context 'close' do
+ before do
+ args[:object_attributes][:state] = 'closed'
+ end
+ it 'returns a message regarding closing of merge requests' do
+ expect(subject.pretext).to eq(
+ 'Test User closed '\
+ 'in : *Issue title*')
+ expect(subject.attachments).to be_empty
+ end
+ end
+end
diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..21fb575480b8fd091bf0a55ac97b4cc91895acfd
--- /dev/null
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -0,0 +1,129 @@
+require 'spec_helper'
+
+describe SlackService::NoteMessage do
+ let(:color) { '#345' }
+
+ before do
+ @args = {
+ user: {
+ name: 'Test User',
+ username: 'username',
+ avatar_url: 'http://fakeavatar'
+ },
+ project_name: 'project_name',
+ project_url: 'somewhere.com',
+ repository: {
+ name: 'project_name',
+ url: 'somewhere.com',
+ },
+ object_attributes: {
+ id: 10,
+ note: 'comment on a commit',
+ url: 'url',
+ noteable_type: 'Commit'
+ }
+ }
+ end
+
+ context 'commit notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on a commit'
+ @args[:object_attributes][:noteable_type] = 'Commit'
+ @args[:commit] = {
+ id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23',
+ message: "Added a commit message\ndetails\n123\n"
+ }
+ end
+
+ it 'returns a message regarding notes on commits' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq("Test User commented on " \
+ " in : " \
+ "*Added a commit message*")
+ expected_attachments = [
+ {
+ text: "comment on a commit",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+
+ context 'merge request notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on a merge request'
+ @args[:object_attributes][:noteable_type] = 'MergeRequest'
+ @args[:merge_request] = {
+ id: 1,
+ iid: 30,
+ title: "merge request title\ndetails\n"
+ }
+ end
+ it 'returns a message regarding notes on a merge request' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq("Test User commented on " \
+ " in : " \
+ "*merge request title*")
+ expected_attachments = [
+ {
+ text: "comment on a merge request",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+
+ context 'issue notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on an issue'
+ @args[:object_attributes][:noteable_type] = 'Issue'
+ @args[:issue] = {
+ id: 1,
+ iid: 20,
+ title: "issue title\ndetails\n"
+ }
+ end
+
+ it 'returns a message regarding notes on an issue' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq(
+ "Test User commented on " \
+ " in : " \
+ "*issue title*")
+ expected_attachments = [
+ {
+ text: "comment on an issue",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+
+ context 'project snippet notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on a snippet'
+ @args[:object_attributes][:noteable_type] = 'Snippet'
+ @args[:snippet] = {
+ id: 5,
+ title: "snippet title\ndetails\n"
+ }
+ end
+
+ it 'returns a message regarding notes on a project snippet' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq("Test User commented on " \
+ " in : " \
+ "*snippet title*")
+ expected_attachments = [
+ {
+ text: "comment on a snippet",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+end
diff --git a/spec/models/slack_message_spec.rb b/spec/models/project_services/slack_service/push_message_spec.rb
similarity index 52%
rename from spec/models/slack_message_spec.rb
rename to spec/models/project_services/slack_service/push_message_spec.rb
index c530fad619b735bef379aa8b0845300caf8b8f17..10963481a1248dfee077ece3c86a6b4edb39f665 100644
--- a/spec/models/slack_message_spec.rb
+++ b/spec/models/project_services/slack_service/push_message_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-describe SlackMessage do
- subject { SlackMessage.new(args) }
+describe SlackService::PushMessage do
+ subject { SlackService::PushMessage.new(args) }
let(:args) {
{
@@ -25,41 +25,64 @@ describe SlackMessage do
end
it 'returns a message regarding pushes' do
- subject.pretext.should ==
+ expect(subject.pretext).to eq(
'user_name pushed to branch of '\
' ()'
- subject.attachments.should == [
+ )
+ expect(subject.attachments).to eq([
{
- text: ": message1 - author1\n"\
- ": message2 - author2",
+ text: ": message1 - author1\n"\
+ ": message2 - author2",
color: color,
}
- ]
+ ])
+ end
+ end
+
+ context 'tag push' do
+ let(:args) {
+ {
+ after: 'after',
+ before: Gitlab::Git::BLANK_SHA,
+ project_name: 'project_name',
+ ref: 'refs/tags/new_tag',
+ user_name: 'user_name',
+ project_url: 'url'
+ }
+ }
+
+ it 'returns a message regarding pushes' do
+ expect(subject.pretext).to eq('user_name pushed new tag ' \
+ ' to ' \
+ '')
+ expect(subject.attachments).to be_empty
end
end
context 'new branch' do
before do
- args[:before] = '000000'
+ args[:before] = Gitlab::Git::BLANK_SHA
end
it 'returns a message regarding a new branch' do
- subject.pretext.should ==
+ expect(subject.pretext).to eq(
'user_name pushed new branch to '\
''
- subject.attachments.should be_empty
+ )
+ expect(subject.attachments).to be_empty
end
end
context 'removed branch' do
before do
- args[:after] = '000000'
+ args[:after] = Gitlab::Git::BLANK_SHA
end
it 'returns a message regarding a removed branch' do
- subject.pretext.should ==
+ expect(subject.pretext).to eq(
'user_name removed branch master from '
- subject.attachments.should be_empty
+ )
+ expect(subject.attachments).to be_empty
end
end
end
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c36506644b30c74508a91abaf5e44357a7d9ad7c
--- /dev/null
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -0,0 +1,170 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe SlackService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :webhook }
+ end
+ end
+
+ describe "Execute" do
+ let(:slack) { SlackService.new }
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+ let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
+ let(:username) { 'slack_username' }
+ let(:channel) { 'slack_channel' }
+
+ before do
+ slack.stub(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+
+ opts = {
+ title: 'Awesome issue',
+ description: 'please fix'
+ }
+
+ issue_service = Issues::CreateService.new(project, user, opts)
+ @issue = issue_service.execute
+ @issues_sample_data = issue_service.hook_data(@issue, 'open')
+
+ opts = {
+ title: 'Awesome merge_request',
+ description: 'please fix',
+ source_branch: 'stable',
+ target_branch: 'master'
+ }
+ merge_service = MergeRequests::CreateService.new(project,
+ user, opts)
+ @merge_request = merge_service.execute
+ @merge_sample_data = merge_service.hook_data(@merge_request,
+ 'open')
+ end
+
+ it "should call Slack API for push events" do
+ slack.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for issue events" do
+ slack.execute(@issues_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for merge requests events" do
+ slack.execute(@merge_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it 'should use the username as an option for slack when configured' do
+ slack.stub(username: username)
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, username: username).
+ and_return(
+ double(:slack_service).as_null_object
+ )
+ slack.execute(push_sample_data)
+ end
+
+ it 'should use the channel as an option when it is configured' do
+ slack.stub(channel: channel)
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, channel: channel).
+ and_return(
+ double(:slack_service).as_null_object
+ )
+ slack.execute(push_sample_data)
+ end
+ end
+
+ describe "Note events" do
+ let(:slack) { SlackService.new }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:snippet) { create(:project_snippet, project: project) }
+ let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
+ let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") }
+ let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")}
+ let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") }
+ let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
+
+ before do
+ slack.stub(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ it "should call Slack API for commit comment events" do
+ data = Gitlab::NoteDataBuilder.build(commit_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for merge request comment events" do
+ data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for issue comment events" do
+ data = Gitlab::NoteDataBuilder.build(issue_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for snippet comment events" do
+ data = Gitlab::NoteDataBuilder.build(snippet_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+ end
+end
diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb
index a6e1d9eef501363d7bf5783cf06796f866a5946b..3e8f106d27fd90e5c32fc839cc80db60ce6bf907 100644
--- a/spec/models/project_snippet_spec.rb
+++ b/spec/models/project_snippet_spec.rb
@@ -19,13 +19,13 @@ require 'spec_helper'
describe ProjectSnippet do
describe "Associations" do
- it { should belong_to(:project) }
+ it { is_expected.to belong_to(:project) }
end
describe "Mass assignment" do
end
describe "Validation" do
- it { should validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:project) }
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 70a15cac1a888bd0644ae2a09513e1b2f8d6eab5..879a63dd9f954585e08e46ce4618ce62e6dd84c5 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -24,103 +24,107 @@
# import_status :string(255)
# repository_size :float default(0.0)
# star_count :integer default(0), not null
+# import_type :string(255)
+# import_source :string(255)
+# avatar :string(255)
#
require 'spec_helper'
describe Project do
- describe "Associations" do
- it { should belong_to(:group) }
- it { should belong_to(:namespace) }
- it { should belong_to(:creator).class_name('User') }
- it { should have_many(:users) }
- it { should have_many(:events).dependent(:destroy) }
- it { should have_many(:merge_requests).dependent(:destroy) }
- it { should have_many(:issues).dependent(:destroy) }
- it { should have_many(:milestones).dependent(:destroy) }
- it { should have_many(:project_members).dependent(:destroy) }
- it { should have_many(:notes).dependent(:destroy) }
- it { should have_many(:snippets).class_name('ProjectSnippet').dependent(:destroy) }
- it { should have_many(:deploy_keys_projects).dependent(:destroy) }
- it { should have_many(:deploy_keys) }
- it { should have_many(:hooks).dependent(:destroy) }
- it { should have_many(:protected_branches).dependent(:destroy) }
- it { should have_one(:forked_project_link).dependent(:destroy) }
- it { should have_one(:slack_service).dependent(:destroy) }
- it { should have_one(:pushover_service).dependent(:destroy) }
+ describe 'Associations' do
+ it { is_expected.to belong_to(:group) }
+ it { is_expected.to belong_to(:namespace) }
+ it { is_expected.to belong_to(:creator).class_name('User') }
+ it { is_expected.to have_many(:users) }
+ it { is_expected.to have_many(:events).dependent(:destroy) }
+ it { is_expected.to have_many(:merge_requests).dependent(:destroy) }
+ it { is_expected.to have_many(:issues).dependent(:destroy) }
+ it { is_expected.to have_many(:milestones).dependent(:destroy) }
+ it { is_expected.to have_many(:project_members).dependent(:destroy) }
+ it { is_expected.to have_many(:notes).dependent(:destroy) }
+ it { is_expected.to have_many(:snippets).class_name('ProjectSnippet').dependent(:destroy) }
+ it { is_expected.to have_many(:deploy_keys_projects).dependent(:destroy) }
+ it { is_expected.to have_many(:deploy_keys) }
+ it { is_expected.to have_many(:hooks).dependent(:destroy) }
+ it { is_expected.to have_many(:protected_branches).dependent(:destroy) }
+ it { is_expected.to have_one(:forked_project_link).dependent(:destroy) }
+ it { is_expected.to have_one(:slack_service).dependent(:destroy) }
+ it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
+ it { is_expected.to have_one(:asana_service).dependent(:destroy) }
end
- describe "Mass assignment" do
+ describe 'Mass assignment' do
end
- describe "Validation" do
+ describe 'Validation' do
let!(:project) { create(:project) }
- it { should validate_presence_of(:name) }
- it { should validate_uniqueness_of(:name).scoped_to(:namespace_id) }
- it { should ensure_length_of(:name).is_within(0..255) }
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
+ it { is_expected.to ensure_length_of(:name).is_within(0..255) }
- it { should validate_presence_of(:path) }
- it { should validate_uniqueness_of(:path).scoped_to(:namespace_id) }
- it { should ensure_length_of(:path).is_within(0..255) }
- it { should ensure_length_of(:description).is_within(0..2000) }
- it { should validate_presence_of(:creator) }
- it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
- it { should validate_presence_of(:namespace) }
+ it { is_expected.to validate_presence_of(:path) }
+ it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
+ it { is_expected.to ensure_length_of(:path).is_within(0..255) }
+ it { is_expected.to ensure_length_of(:description).is_within(0..2000) }
+ it { is_expected.to validate_presence_of(:creator) }
+ it { is_expected.to ensure_length_of(:issues_tracker_id).is_within(0..255) }
+ it { is_expected.to validate_presence_of(:namespace) }
- it "should not allow new projects beyond user limits" do
+ it 'should not allow new projects beyond user limits' do
project2 = build(:project)
- project2.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
- project2.should_not be_valid
- project2.errors[:limit_reached].first.should match(/Your project limit is 0/)
+ allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
+ expect(project2).not_to be_valid
+ expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/)
end
end
- describe "Respond to" do
- it { should respond_to(:url_to_repo) }
- it { should respond_to(:repo_exists?) }
- it { should respond_to(:satellite) }
- it { should respond_to(:update_merge_requests) }
- it { should respond_to(:execute_hooks) }
- it { should respond_to(:name_with_namespace) }
- it { should respond_to(:owner) }
- it { should respond_to(:path_with_namespace) }
+ describe 'Respond to' do
+ it { is_expected.to respond_to(:url_to_repo) }
+ it { is_expected.to respond_to(:repo_exists?) }
+ it { is_expected.to respond_to(:satellite) }
+ it { is_expected.to respond_to(:update_merge_requests) }
+ it { is_expected.to respond_to(:execute_hooks) }
+ it { is_expected.to respond_to(:name_with_namespace) }
+ it { is_expected.to respond_to(:owner) }
+ it { is_expected.to respond_to(:path_with_namespace) }
end
- it "should return valid url to repo" do
- project = Project.new(path: "somewhere")
- project.url_to_repo.should == Gitlab.config.gitlab_shell.ssh_path_prefix + "somewhere.git"
+ it 'should return valid url to repo' do
+ project = Project.new(path: 'somewhere')
+ expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
end
- it "returns the full web URL for this repo" do
- project = Project.new(path: "somewhere")
- project.web_url.should == "#{Gitlab.config.gitlab.url}/somewhere"
+ it 'returns the full web URL for this repo' do
+ project = Project.new(path: 'somewhere')
+ expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/somewhere")
end
- it "returns the web URL without the protocol for this repo" do
- project = Project.new(path: "somewhere")
- project.web_url_without_protocol.should == "#{Gitlab.config.gitlab.url.split("://")[1]}/somewhere"
+ it 'returns the web URL without the protocol for this repo' do
+ project = Project.new(path: 'somewhere')
+ expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere")
end
- describe "last_activity methods" do
+ describe 'last_activity methods' do
let(:project) { create(:project) }
let(:last_event) { double(created_at: Time.now) }
- describe "last_activity" do
- it "should alias last_activity to last_event" do
+ describe 'last_activity' do
+ it 'should alias last_activity to last_event' do
project.stub(last_event: last_event)
- project.last_activity.should == last_event
+ expect(project.last_activity).to eq(last_event)
end
end
describe 'last_activity_date' do
it 'returns the creation date of the project\'s last event if present' do
last_activity_event = create(:event, project: project)
- project.last_activity_at.to_i.should == last_event.created_at.to_i
+ expect(project.last_activity_at.to_i).to eq(last_event.created_at.to_i)
end
it 'returns the project\'s last update date if it has no events' do
- project.last_activity_date.should == project.updated_at
+ expect(project.last_activity_date).to eq(project.updated_at)
end
end
end
@@ -132,16 +136,16 @@ describe Project do
let(:prev_commit_id) { merge_request.commits.last.id }
let(:commit_id) { merge_request.commits.first.id }
- it "should close merge request if last commit from source branch was pushed to target branch" do
+ it 'should close merge request if last commit from source branch was pushed to target branch' do
project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user)
merge_request.reload
- merge_request.merged?.should be_true
+ expect(merge_request.merged?).to be_truthy
end
- it "should update merge request commits with new one if pushed to source branch" do
+ it 'should update merge request commits with new one if pushed to source branch' do
project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user)
merge_request.reload
- merge_request.last_commit.id.should == commit_id
+ expect(merge_request.last_commit.id).to eq(commit_id)
end
end
@@ -152,8 +156,8 @@ describe Project do
@project = create(:project, name: 'gitlabhq', namespace: @group)
end
- it { Project.find_with_namespace('gitlab/gitlabhq').should == @project }
- it { Project.find_with_namespace('gitlab-ci').should be_nil }
+ it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) }
+ it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil }
end
end
@@ -164,15 +168,15 @@ describe Project do
@project = create(:project, name: 'gitlabhq', namespace: @group)
end
- it { @project.to_param.should == "gitlab/gitlabhq" }
+ it { expect(@project.to_param).to eq('gitlabhq') }
end
end
describe :repository do
let(:project) { create(:project) }
- it "should return valid repo" do
- project.repository.should be_kind_of(Repository)
+ it 'should return valid repo' do
+ expect(project.repository).to be_kind_of(Repository)
end
end
@@ -182,29 +186,29 @@ describe Project do
let(:not_existed_issue) { create(:issue) }
let(:ext_project) { create(:redmine_project) }
- it "should be true or if used internal tracker and issue exists" do
- project.issue_exists?(existed_issue.iid).should be_true
+ it 'should be true or if used internal tracker and issue exists' do
+ expect(project.issue_exists?(existed_issue.iid)).to be_truthy
end
- it "should be false or if used internal tracker and issue not exists" do
- project.issue_exists?(not_existed_issue.iid).should be_false
+ it 'should be false or if used internal tracker and issue not exists' do
+ expect(project.issue_exists?(not_existed_issue.iid)).to be_falsey
end
- it "should always be true if used other tracker" do
- ext_project.issue_exists?(rand(100)).should be_true
+ it 'should always be true if used other tracker' do
+ expect(ext_project.issue_exists?(rand(100))).to be_truthy
end
end
- describe :used_default_issues_tracker? do
+ describe :default_issues_tracker? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
it "should be true if used internal tracker" do
- project.used_default_issues_tracker?.should be_true
+ expect(project.default_issues_tracker?).to be_truthy
end
it "should be false if used other tracker" do
- ext_project.used_default_issues_tracker?.should be_false
+ expect(ext_project.default_issues_tracker?).to be_falsey
end
end
@@ -212,20 +216,20 @@ describe Project do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
- it "should be true for projects with external issues tracker if issues enabled" do
- ext_project.can_have_issues_tracker_id?.should be_true
+ it 'should be true for projects with external issues tracker if issues enabled' do
+ expect(ext_project.can_have_issues_tracker_id?).to be_truthy
end
- it "should be false for projects with internal issue tracker if issues enabled" do
- project.can_have_issues_tracker_id?.should be_false
+ it 'should be false for projects with internal issue tracker if issues enabled' do
+ expect(project.can_have_issues_tracker_id?).to be_falsey
end
- it "should be always false if issues disabled" do
+ it 'should be always false if issues disabled' do
project.issues_enabled = false
ext_project.issues_enabled = false
- project.can_have_issues_tracker_id?.should be_false
- ext_project.can_have_issues_tracker_id?.should be_false
+ expect(project.can_have_issues_tracker_id?).to be_falsey
+ expect(ext_project.can_have_issues_tracker_id?).to be_falsey
end
end
@@ -236,8 +240,8 @@ describe Project do
project.protected_branches.create(name: 'master')
end
- it { project.open_branches.map(&:name).should include('feature') }
- it { project.open_branches.map(&:name).should_not include('master') }
+ it { expect(project.open_branches.map(&:name)).to include('feature') }
+ it { expect(project.open_branches.map(&:name)).not_to include('master') }
end
describe '#star_count' do
@@ -308,4 +312,49 @@ describe Project do
expect(project.star_count).to eq(0)
end
end
+
+ describe :avatar_type do
+ let(:project) { create(:project) }
+
+ it 'should be true if avatar is image' do
+ project.update_attribute(:avatar, 'uploads/avatar.png')
+ expect(project.avatar_type).to be_truthy
+ end
+
+ it 'should be false if avatar is html page' do
+ project.update_attribute(:avatar, 'uploads/avatar.html')
+ expect(project.avatar_type).to eq(['only images allowed'])
+ end
+ end
+
+ describe :avatar_url do
+ subject { project.avatar_url }
+
+ let(:project) { create(:project) }
+
+ context 'When avatar file is uploaded' do
+ before do
+ project.update_columns(avatar: 'uploads/avatar.png')
+ allow(project.avatar).to receive(:present?) { true }
+ end
+
+ let(:avatar_path) do
+ "/uploads/project/avatar/#{project.id}/uploads/avatar.png"
+ end
+
+ it { should eq "http://localhost#{avatar_path}" }
+ end
+
+ context 'When avatar file in git' do
+ before do
+ allow(project).to receive(:avatar_in_git) { true }
+ end
+
+ let(:avatar_path) do
+ "/#{project.namespace.name}/#{project.path}/avatar"
+ end
+
+ it { should eq "http://localhost#{avatar_path}" }
+ end
+ end
end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index bbf50b654f4379d80904f949c1c985efea8081ce..19201cc15a7f474ef27e6f3c0d0db6f885b73f92 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -16,19 +16,19 @@ describe ProjectTeam do
end
describe 'members collection' do
- it { project.team.masters.should include(master) }
- it { project.team.masters.should_not include(guest) }
- it { project.team.masters.should_not include(reporter) }
- it { project.team.masters.should_not include(nonmember) }
+ it { expect(project.team.masters).to include(master) }
+ it { expect(project.team.masters).not_to include(guest) }
+ it { expect(project.team.masters).not_to include(reporter) }
+ it { expect(project.team.masters).not_to include(nonmember) }
end
describe 'access methods' do
- it { project.team.master?(master).should be_true }
- it { project.team.master?(guest).should be_false }
- it { project.team.master?(reporter).should be_false }
- it { project.team.master?(nonmember).should be_false }
- it { project.team.member?(nonmember).should be_false }
- it { project.team.member?(guest).should be_true }
+ it { expect(project.team.master?(master)).to be_truthy }
+ it { expect(project.team.master?(guest)).to be_falsey }
+ it { expect(project.team.master?(reporter)).to be_falsey }
+ it { expect(project.team.master?(nonmember)).to be_falsey }
+ it { expect(project.team.member?(nonmember)).to be_falsey }
+ it { expect(project.team.member?(guest)).to be_truthy }
end
end
@@ -49,21 +49,21 @@ describe ProjectTeam do
end
describe 'members collection' do
- it { project.team.reporters.should include(reporter) }
- it { project.team.masters.should include(master) }
- it { project.team.masters.should include(guest) }
- it { project.team.masters.should_not include(reporter) }
- it { project.team.masters.should_not include(nonmember) }
+ it { expect(project.team.reporters).to include(reporter) }
+ it { expect(project.team.masters).to include(master) }
+ it { expect(project.team.masters).to include(guest) }
+ it { expect(project.team.masters).not_to include(reporter) }
+ it { expect(project.team.masters).not_to include(nonmember) }
end
describe 'access methods' do
- it { project.team.reporter?(reporter).should be_true }
- it { project.team.master?(master).should be_true }
- it { project.team.master?(guest).should be_true }
- it { project.team.master?(reporter).should be_false }
- it { project.team.master?(nonmember).should be_false }
- it { project.team.member?(nonmember).should be_false }
- it { project.team.member?(guest).should be_true }
+ it { expect(project.team.reporter?(reporter)).to be_truthy }
+ it { expect(project.team.master?(master)).to be_truthy }
+ it { expect(project.team.master?(guest)).to be_truthy }
+ it { expect(project.team.master?(reporter)).to be_falsey }
+ it { expect(project.team.master?(nonmember)).to be_falsey }
+ it { expect(project.team.member?(nonmember)).to be_falsey }
+ it { expect(project.team.member?(guest)).to be_truthy }
end
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index e4ee2fc5b1394b13e360f0734d274805f2943f6d..2acdb7dfddcba5af30abeda55bd60ece92488a13 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -12,19 +12,19 @@ describe ProjectWiki do
describe "#path_with_namespace" do
it "returns the project path with namespace with the .wiki extension" do
- subject.path_with_namespace.should == project.path_with_namespace + ".wiki"
+ expect(subject.path_with_namespace).to eq(project.path_with_namespace + ".wiki")
end
end
describe "#url_to_repo" do
it "returns the correct ssh url to the repo" do
- subject.url_to_repo.should == gitlab_shell.url_to_repo(subject.path_with_namespace)
+ expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace))
end
end
describe "#ssh_url_to_repo" do
it "equals #url_to_repo" do
- subject.ssh_url_to_repo.should == subject.url_to_repo
+ expect(subject.ssh_url_to_repo).to eq(subject.url_to_repo)
end
end
@@ -32,21 +32,21 @@ describe ProjectWiki do
it "provides the full http url to the repo" do
gitlab_url = Gitlab.config.gitlab.url
repo_http_url = "#{gitlab_url}/#{subject.path_with_namespace}.git"
- subject.http_url_to_repo.should == repo_http_url
+ expect(subject.http_url_to_repo).to eq(repo_http_url)
end
end
describe "#wiki" do
it "contains a Gollum::Wiki instance" do
- subject.wiki.should be_a Gollum::Wiki
+ expect(subject.wiki).to be_a Gollum::Wiki
end
it "creates a new wiki repo if one does not yet exist" do
- project_wiki.create_page("index", "test content").should be_true
+ expect(project_wiki.create_page("index", "test content")).to be_truthy
end
it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
- project_wiki.stub(:init_repo).and_return(false)
+ allow(project_wiki).to receive(:init_repo).and_return(false)
expect { project_wiki.send(:create_repo!) }.to raise_exception(ProjectWiki::CouldNotCreateWikiError)
end
end
@@ -54,21 +54,27 @@ describe ProjectWiki do
describe "#empty?" do
context "when the wiki repository is empty" do
before do
- Gitlab::Shell.any_instance.stub(:add_repository) do
+ allow_any_instance_of(Gitlab::Shell).to receive(:add_repository) do
create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git")
end
- project.stub(:path_with_namespace).and_return("non-existant")
+ allow(project).to receive(:path_with_namespace).and_return("non-existant")
end
- its(:empty?) { should be_true }
+ describe '#empty?' do
+ subject { super().empty? }
+ it { is_expected.to be_truthy }
+ end
end
context "when the wiki has pages" do
before do
- create_page("index", "This is an awesome new Gollum Wiki")
+ project_wiki.create_page("index", "This is an awesome new Gollum Wiki")
end
- its(:empty?) { should be_false }
+ describe '#empty?' do
+ subject { super().empty? }
+ it { is_expected.to be_falsey }
+ end
end
end
@@ -83,11 +89,11 @@ describe ProjectWiki do
end
it "returns an array of WikiPage instances" do
- @pages.first.should be_a WikiPage
+ expect(@pages.first).to be_a WikiPage
end
it "returns the correct number of pages" do
- @pages.count.should == 1
+ expect(@pages.count).to eq(1)
end
end
@@ -102,55 +108,55 @@ describe ProjectWiki do
it "returns the latest version of the page if it exists" do
page = subject.find_page("index page")
- page.title.should == "index page"
+ expect(page.title).to eq("index page")
end
it "returns nil if the page does not exist" do
- subject.find_page("non-existant").should == nil
+ expect(subject.find_page("non-existant")).to eq(nil)
end
it "can find a page by slug" do
page = subject.find_page("index-page")
- page.title.should == "index page"
+ expect(page.title).to eq("index page")
end
it "returns a WikiPage instance" do
page = subject.find_page("index page")
- page.should be_a WikiPage
+ expect(page).to be_a WikiPage
end
end
describe '#find_file' do
before do
file = Gollum::File.new(subject.wiki)
- Gollum::Wiki.any_instance.
- stub(:file).with('image.jpg', 'master', true).
+ allow_any_instance_of(Gollum::Wiki).
+ to receive(:file).with('image.jpg', 'master', true).
and_return(file)
- Gollum::File.any_instance.
- stub(:mime_type).
+ allow_any_instance_of(Gollum::File).
+ to receive(:mime_type).
and_return('image/jpeg')
- Gollum::Wiki.any_instance.
- stub(:file).with('non-existant', 'master', true).
+ allow_any_instance_of(Gollum::Wiki).
+ to receive(:file).with('non-existant', 'master', true).
and_return(nil)
end
after do
- Gollum::Wiki.any_instance.unstub(:file)
- Gollum::File.any_instance.unstub(:mime_type)
+ allow_any_instance_of(Gollum::Wiki).to receive(:file).and_call_original
+ allow_any_instance_of(Gollum::File).to receive(:mime_type).and_call_original
end
it 'returns the latest version of the file if it exists' do
file = subject.find_file('image.jpg')
- file.mime_type.should == 'image/jpeg'
+ expect(file.mime_type).to eq('image/jpeg')
end
it 'returns nil if the page does not exist' do
- subject.find_file('non-existant').should == nil
+ expect(subject.find_file('non-existant')).to eq(nil)
end
it 'returns a Gollum::File instance' do
file = subject.find_file('image.jpg')
- file.should be_a Gollum::File
+ expect(file).to be_a Gollum::File
end
end
@@ -160,23 +166,23 @@ describe ProjectWiki do
end
it "creates a new wiki page" do
- subject.create_page("test page", "this is content").should_not == false
- subject.pages.count.should == 1
+ expect(subject.create_page("test page", "this is content")).not_to eq(false)
+ expect(subject.pages.count).to eq(1)
end
it "returns false when a duplicate page exists" do
subject.create_page("test page", "content")
- subject.create_page("test page", "content").should == false
+ expect(subject.create_page("test page", "content")).to eq(false)
end
it "stores an error message when a duplicate page exists" do
2.times { subject.create_page("test page", "content") }
- subject.error_message.should =~ /Duplicate page:/
+ expect(subject.error_message).to match(/Duplicate page:/)
end
it "sets the correct commit message" do
subject.create_page("test page", "some content", :markdown, "commit message")
- subject.pages.first.page.version.message.should == "commit message"
+ expect(subject.pages.first.page.version.message).to eq("commit message")
end
end
@@ -193,11 +199,11 @@ describe ProjectWiki do
end
it "updates the content of the page" do
- @page.raw_data.should == "some other content"
+ expect(@page.raw_data).to eq("some other content")
end
it "sets the correct commit message" do
- @page.version.message.should == "updated page"
+ expect(@page.version.message).to eq("updated page")
end
end
@@ -209,7 +215,7 @@ describe ProjectWiki do
it "deletes the page" do
subject.delete_page(@page)
- subject.pages.count.should == 0
+ expect(subject.pages.count).to eq(0)
end
end
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index af48c2c6d9e8439b2b2616caa401aeee76835664..1e6937b536c418581c8800c7ab348c6116cc46c1 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -2,25 +2,26 @@
#
# Table name: protected_branches
#
-# id :integer not null, primary key
-# project_id :integer not null
-# name :string(255) not null
-# created_at :datetime
-# updated_at :datetime
+# id :integer not null, primary key
+# project_id :integer not null
+# name :string(255) not null
+# created_at :datetime
+# updated_at :datetime
+# developers_can_push :boolean default(FALSE), not null
#
require 'spec_helper'
describe ProtectedBranch do
describe 'Associations' do
- it { should belong_to(:project) }
+ it { is_expected.to belong_to(:project) }
end
describe "Mass assignment" do
end
describe 'Validation' do
- it { should validate_presence_of(:project) }
- it { should validate_presence_of(:name) }
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:name) }
end
end
diff --git a/spec/models/pushover_service_spec.rb b/spec/models/pushover_service_spec.rb
deleted file mode 100644
index 59db69d7572f567cec17b850bcb8c0f6684c9926..0000000000000000000000000000000000000000
--- a/spec/models/pushover_service_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe PushoverService do
- describe 'Associations' do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe 'Validations' do
- context 'active' do
- before do
- subject.active = true
- end
-
- it { should validate_presence_of :api_key }
- it { should validate_presence_of :user_key }
- it { should validate_presence_of :priority }
- end
- end
-
- describe 'Execute' do
- let(:pushover) { PushoverService.new }
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:sample_data) { GitPushService.new.sample_data(project, user) }
-
- let(:api_key) { 'verySecret' }
- let(:user_key) { 'verySecret' }
- let(:device) { 'myDevice' }
- let(:priority) { 0 }
- let(:sound) { 'bike' }
- let(:api_url) { 'https://api.pushover.net/1/messages.json' }
-
- before do
- pushover.stub(
- project: project,
- project_id: project.id,
- service_hook: true,
- api_key: api_key,
- user_key: user_key,
- device: device,
- priority: priority,
- sound: sound
- )
-
- WebMock.stub_request(:post, api_url)
- end
-
- it 'should call Pushover API' do
- pushover.execute(sample_data)
-
- WebMock.should have_requested(:post, api_url).once
- end
- end
-end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 6c3e221f34372200cf8af9171d8b38578f61be10..f41e5a97ca34d1b9de26c90ae607b0c884efc567 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -8,14 +8,21 @@ describe Repository do
describe :branch_names_contains do
subject { repository.branch_names_contains(sample_commit.id) }
- it { should include('master') }
- it { should_not include('feature') }
- it { should_not include('fix') }
+ it { is_expected.to include('master') }
+ it { is_expected.not_to include('feature') }
+ it { is_expected.not_to include('fix') }
+ end
+
+ describe :tag_names_contains do
+ subject { repository.tag_names_contains(sample_commit.id) }
+
+ it { is_expected.to include('v1.1.0') }
+ it { is_expected.not_to include('v1.0.0') }
end
describe :last_commit_for_path do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
- it { should eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
+ it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index c96f2b205292150db3e3858abf64f02fd53f1e20..735652aea78e496cedfe441133218de6f66fec96 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -2,14 +2,19 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
#
require 'spec_helper'
@@ -17,8 +22,8 @@ require 'spec_helper'
describe Service do
describe "Associations" do
- it { should belong_to :project }
- it { should have_one :service_hook }
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
end
describe "Mass assignment" do
@@ -40,7 +45,7 @@ describe Service do
end
describe :can_test do
- it { @testable.should == true }
+ it { expect(@testable).to eq(true) }
end
end
@@ -55,7 +60,32 @@ describe Service do
end
describe :can_test do
- it { @testable.should == true }
+ it { expect(@testable).to eq(true) }
+ end
+ end
+ end
+
+ describe "Template" do
+ describe "for pushover service" do
+ let(:service_template) {
+ PushoverService.create(template: true, properties: {device: 'MyDevice', sound: 'mic', priority: 4, api_key: '123456789'})
+ }
+ let(:project) { create(:project) }
+
+ describe 'should be prefilled for projects pushover service' do
+ before do
+ service_template
+ project.build_missing_services
+ end
+
+ it "should have all fields prefilled" do
+ service = project.pushover_service
+ expect(service.template).to eq(false)
+ expect(service.device).to eq('MyDevice')
+ expect(service.sound).to eq('mic')
+ expect(service.priority).to eq(4)
+ expect(service.api_key).to eq('123456789')
+ end
end
end
end
diff --git a/spec/models/slack_service_spec.rb b/spec/models/slack_service_spec.rb
deleted file mode 100644
index d48403919676414a3e0ea855ee3abf9608ca401f..0000000000000000000000000000000000000000
--- a/spec/models/slack_service_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-#
-
-require 'spec_helper'
-
-describe SlackService do
- describe "Associations" do
- it { should belong_to :project }
- it { should have_one :service_hook }
- end
-
- describe "Validations" do
- context "active" do
- before do
- subject.active = true
- end
-
- it { should validate_presence_of :webhook }
- end
- end
-
- describe "Execute" do
- let(:slack) { SlackService.new }
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:sample_data) { GitPushService.new.sample_data(project, user) }
- let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
-
- before do
- slack.stub(
- project: project,
- project_id: project.id,
- service_hook: true,
- webhook: webhook_url
- )
-
- WebMock.stub_request(:post, webhook_url)
- end
-
- it "should call Slack API" do
- slack.execute(sample_data)
-
- WebMock.should have_requested(:post, webhook_url).once
- end
- end
-end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 1ef2c512c1ff9c4e9f766fb76ab0fdef32e7f07e..e37dcc752306869e34bff90528367d313069e600 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -19,22 +19,22 @@ require 'spec_helper'
describe Snippet do
describe "Associations" do
- it { should belong_to(:author).class_name('User') }
- it { should have_many(:notes).dependent(:destroy) }
+ it { is_expected.to belong_to(:author).class_name('User') }
+ it { is_expected.to have_many(:notes).dependent(:destroy) }
end
describe "Mass assignment" do
end
describe "Validation" do
- it { should validate_presence_of(:author) }
+ it { is_expected.to validate_presence_of(:author) }
- it { should validate_presence_of(:title) }
- it { should ensure_length_of(:title).is_within(0..255) }
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to ensure_length_of(:title).is_within(0..255) }
- it { should validate_presence_of(:file_name) }
- it { should ensure_length_of(:title).is_within(0..255) }
+ it { is_expected.to validate_presence_of(:file_name) }
+ it { is_expected.to ensure_length_of(:title).is_within(0..255) }
- it { should validate_presence_of(:content) }
+ it { is_expected.to validate_presence_of(:content) }
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 6d865cfc691ce144caa8cd14c0c3f01d714c4f1f..10e90cae1430971becda8f98f2a1f11027753895 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2,79 +2,85 @@
#
# Table name: users
#
-# id :integer not null, primary key
-# email :string(255) default(""), not null
-# encrypted_password :string(255) default(""), not null
-# reset_password_token :string(255)
-# reset_password_sent_at :datetime
-# remember_created_at :datetime
-# sign_in_count :integer default(0)
-# current_sign_in_at :datetime
-# last_sign_in_at :datetime
-# current_sign_in_ip :string(255)
-# last_sign_in_ip :string(255)
-# created_at :datetime
-# updated_at :datetime
-# name :string(255)
-# admin :boolean default(FALSE), not null
-# projects_limit :integer default(10)
-# skype :string(255) default(""), not null
-# linkedin :string(255) default(""), not null
-# twitter :string(255) default(""), not null
-# authentication_token :string(255)
-# theme_id :integer default(1), not null
-# bio :string(255)
-# failed_attempts :integer default(0)
-# locked_at :datetime
-# extern_uid :string(255)
-# provider :string(255)
-# username :string(255)
-# can_create_group :boolean default(TRUE), not null
-# can_create_team :boolean default(TRUE), not null
-# state :string(255)
-# color_scheme_id :integer default(1), not null
-# notification_level :integer default(1), not null
-# password_expires_at :datetime
-# created_by_id :integer
-# last_credential_check_at :datetime
-# avatar :string(255)
-# confirmation_token :string(255)
-# confirmed_at :datetime
-# confirmation_sent_at :datetime
-# unconfirmed_email :string(255)
-# hide_no_ssh_key :boolean default(FALSE)
-# website_url :string(255) default(""), not null
+# id :integer not null, primary key
+# email :string(255) default(""), not null
+# encrypted_password :string(255) default(""), not null
+# reset_password_token :string(255)
+# reset_password_sent_at :datetime
+# remember_created_at :datetime
+# sign_in_count :integer default(0)
+# current_sign_in_at :datetime
+# last_sign_in_at :datetime
+# current_sign_in_ip :string(255)
+# last_sign_in_ip :string(255)
+# created_at :datetime
+# updated_at :datetime
+# name :string(255)
+# admin :boolean default(FALSE), not null
+# projects_limit :integer default(10)
+# skype :string(255) default(""), not null
+# linkedin :string(255) default(""), not null
+# twitter :string(255) default(""), not null
+# authentication_token :string(255)
+# theme_id :integer default(1), not null
+# bio :string(255)
+# failed_attempts :integer default(0)
+# locked_at :datetime
+# username :string(255)
+# can_create_group :boolean default(TRUE), not null
+# can_create_team :boolean default(TRUE), not null
+# state :string(255)
+# color_scheme_id :integer default(1), not null
+# notification_level :integer default(1), not null
+# password_expires_at :datetime
+# created_by_id :integer
+# last_credential_check_at :datetime
+# avatar :string(255)
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# confirmation_sent_at :datetime
+# unconfirmed_email :string(255)
+# hide_no_ssh_key :boolean default(FALSE)
+# website_url :string(255) default(""), not null
+# github_access_token :string(255)
+# gitlab_access_token :string(255)
+# notification_email :string(255)
+# hide_no_password :boolean default(FALSE)
+# password_automatically_set :boolean default(FALSE)
+# bitbucket_access_token :string(255)
+# bitbucket_access_token_secret :string(255)
#
require 'spec_helper'
describe User do
describe "Associations" do
- it { should have_one(:namespace) }
- it { should have_many(:snippets).class_name('Snippet').dependent(:destroy) }
- it { should have_many(:project_members).dependent(:destroy) }
- it { should have_many(:groups) }
- it { should have_many(:keys).dependent(:destroy) }
- it { should have_many(:events).class_name('Event').dependent(:destroy) }
- it { should have_many(:recent_events).class_name('Event') }
- it { should have_many(:issues).dependent(:destroy) }
- it { should have_many(:notes).dependent(:destroy) }
- it { should have_many(:assigned_issues).dependent(:destroy) }
- it { should have_many(:merge_requests).dependent(:destroy) }
- it { should have_many(:assigned_merge_requests).dependent(:destroy) }
+ it { is_expected.to have_one(:namespace) }
+ it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) }
+ it { is_expected.to have_many(:project_members).dependent(:destroy) }
+ it { is_expected.to have_many(:groups) }
+ it { is_expected.to have_many(:keys).dependent(:destroy) }
+ it { is_expected.to have_many(:events).class_name('Event').dependent(:destroy) }
+ it { is_expected.to have_many(:recent_events).class_name('Event') }
+ it { is_expected.to have_many(:issues).dependent(:destroy) }
+ it { is_expected.to have_many(:notes).dependent(:destroy) }
+ it { is_expected.to have_many(:assigned_issues).dependent(:destroy) }
+ it { is_expected.to have_many(:merge_requests).dependent(:destroy) }
+ it { is_expected.to have_many(:assigned_merge_requests).dependent(:destroy) }
+ it { is_expected.to have_many(:identities).dependent(:destroy) }
end
describe "Mass assignment" do
end
describe 'validations' do
- it { should validate_presence_of(:username) }
- it { should validate_presence_of(:projects_limit) }
- it { should validate_numericality_of(:projects_limit) }
- it { should allow_value(0).for(:projects_limit) }
- it { should_not allow_value(-1).for(:projects_limit) }
+ it { is_expected.to validate_presence_of(:username) }
+ it { is_expected.to validate_presence_of(:projects_limit) }
+ it { is_expected.to validate_numericality_of(:projects_limit) }
+ it { is_expected.to allow_value(0).for(:projects_limit) }
+ it { is_expected.not_to allow_value(-1).for(:projects_limit) }
- it { should ensure_length_of(:bio).is_within(0..255) }
+ it { is_expected.to ensure_length_of(:bio).is_within(0..255) }
describe 'email' do
it 'accepts info@example.com' do
@@ -110,34 +116,34 @@ describe User do
end
describe "Respond to" do
- it { should respond_to(:is_admin?) }
- it { should respond_to(:name) }
- it { should respond_to(:private_token) }
+ it { is_expected.to respond_to(:is_admin?) }
+ it { is_expected.to respond_to(:name) }
+ it { is_expected.to respond_to(:private_token) }
end
describe '#generate_password' do
it "should execute callback when force_random_password specified" do
user = build(:user, force_random_password: true)
- user.should_receive(:generate_password)
+ expect(user).to receive(:generate_password)
user.save
end
it "should not generate password by default" do
user = create(:user, password: 'abcdefghe')
- user.password.should == 'abcdefghe'
+ expect(user.password).to eq('abcdefghe')
end
it "should generate password when forcing random password" do
- Devise.stub(:friendly_token).and_return('123456789')
+ allow(Devise).to receive(:friendly_token).and_return('123456789')
user = create(:user, password: 'abcdefg', force_random_password: true)
- user.password.should == '12345678'
+ expect(user.password).to eq('12345678')
end
end
describe 'authentication token' do
it "should have authentication token" do
user = create(:user)
- user.authentication_token.should_not be_blank
+ expect(user.authentication_token).not_to be_blank
end
end
@@ -152,15 +158,15 @@ describe User do
@project_3.team << [@user, :developer]
end
- it { @user.authorized_projects.should include(@project) }
- it { @user.authorized_projects.should include(@project_2) }
- it { @user.authorized_projects.should include(@project_3) }
- it { @user.owned_projects.should include(@project) }
- it { @user.owned_projects.should_not include(@project_2) }
- it { @user.owned_projects.should_not include(@project_3) }
- it { @user.personal_projects.should include(@project) }
- it { @user.personal_projects.should_not include(@project_2) }
- it { @user.personal_projects.should_not include(@project_3) }
+ it { expect(@user.authorized_projects).to include(@project) }
+ it { expect(@user.authorized_projects).to include(@project_2) }
+ it { expect(@user.authorized_projects).to include(@project_3) }
+ it { expect(@user.owned_projects).to include(@project) }
+ it { expect(@user.owned_projects).not_to include(@project_2) }
+ it { expect(@user.owned_projects).not_to include(@project_3) }
+ it { expect(@user.personal_projects).to include(@project) }
+ it { expect(@user.personal_projects).not_to include(@project_2) }
+ it { expect(@user.personal_projects).not_to include(@project_3) }
end
describe 'groups' do
@@ -170,9 +176,9 @@ describe User do
@group.add_owner(@user)
end
- it { @user.several_namespaces?.should be_true }
- it { @user.authorized_groups.should == [@group] }
- it { @user.owned_groups.should == [@group] }
+ it { expect(@user.several_namespaces?).to be_truthy }
+ it { expect(@user.authorized_groups).to eq([@group]) }
+ it { expect(@user.owned_groups).to eq([@group]) }
end
describe 'group multiple owners' do
@@ -185,7 +191,7 @@ describe User do
@group.add_user(@user2, GroupMember::OWNER)
end
- it { @user2.several_namespaces?.should be_true }
+ it { expect(@user2.several_namespaces?).to be_truthy }
end
describe 'namespaced' do
@@ -194,7 +200,7 @@ describe User do
@project = create :project, namespace: @user.namespace
end
- it { @user.several_namespaces?.should be_false }
+ it { expect(@user.several_namespaces?).to be_falsey }
end
describe 'blocking user' do
@@ -202,7 +208,7 @@ describe User do
it "should block user" do
user.block
- user.blocked?.should be_true
+ expect(user.blocked?).to be_truthy
end
end
@@ -214,10 +220,10 @@ describe User do
@blocked = create :user, state: :blocked
end
- it { User.filter("admins").should == [@admin] }
- it { User.filter("blocked").should == [@blocked] }
- it { User.filter("wop").should include(@user, @admin, @blocked) }
- it { User.filter(nil).should include(@user, @admin) }
+ it { expect(User.filter("admins")).to eq([@admin]) }
+ it { expect(User.filter("blocked")).to eq([@blocked]) }
+ it { expect(User.filter("wop")).to include(@user, @admin, @blocked) }
+ it { expect(User.filter(nil)).to include(@user, @admin) }
end
describe :not_in_project do
@@ -227,27 +233,27 @@ describe User do
@project = create :project
end
- it { User.not_in_project(@project).should include(@user, @project.owner) }
+ it { expect(User.not_in_project(@project)).to include(@user, @project.owner) }
end
describe 'user creation' do
describe 'normal user' do
let(:user) { create(:user, name: 'John Smith') }
- it { user.is_admin?.should be_false }
- it { user.require_ssh_key?.should be_true }
- it { user.can_create_group?.should be_true }
- it { user.can_create_project?.should be_true }
- it { user.first_name.should == 'John' }
+ it { expect(user.is_admin?).to be_falsey }
+ it { expect(user.require_ssh_key?).to be_truthy }
+ it { expect(user.can_create_group?).to be_truthy }
+ it { expect(user.can_create_project?).to be_truthy }
+ it { expect(user.first_name).to eq('John') }
end
describe 'with defaults' do
let(:user) { User.new }
it "should apply defaults to user" do
- user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit
- user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group
- user.theme_id.should == Gitlab.config.gitlab.default_theme
+ expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit)
+ expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group)
+ expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme)
end
end
@@ -255,9 +261,9 @@ describe User do
let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: Gitlab::Theme::BASIC) }
it "should apply defaults to user" do
- user.projects_limit.should == 123
- user.can_create_group.should be_false
- user.theme_id.should == Gitlab::Theme::BASIC
+ expect(user.projects_limit).to eq(123)
+ expect(user.can_create_group).to be_falsey
+ expect(user.theme_id).to eq(Gitlab::Theme::BASIC)
end
end
end
@@ -267,12 +273,12 @@ describe User do
let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
it "should be case insensitive" do
- User.search(user1.username.upcase).to_a.should == [user1]
- User.search(user1.username.downcase).to_a.should == [user1]
- User.search(user2.username.upcase).to_a.should == [user2]
- User.search(user2.username.downcase).to_a.should == [user2]
- User.search(user1.username.downcase).to_a.count.should == 2
- User.search(user2.username.downcase).to_a.count.should == 1
+ expect(User.search(user1.username.upcase).to_a).to eq([user1])
+ expect(User.search(user1.username.downcase).to_a).to eq([user1])
+ expect(User.search(user2.username.upcase).to_a).to eq([user2])
+ expect(User.search(user2.username.downcase).to_a).to eq([user2])
+ expect(User.search(user1.username.downcase).to_a.count).to eq(2)
+ expect(User.search(user2.username.downcase).to_a.count).to eq(1)
end
end
@@ -280,10 +286,10 @@ describe User do
let(:user1) { create(:user, username: 'foo') }
it "should get the correct user" do
- User.by_username_or_id(user1.id).should == user1
- User.by_username_or_id('foo').should == user1
- User.by_username_or_id(-1).should be_nil
- User.by_username_or_id('bar').should be_nil
+ expect(User.by_username_or_id(user1.id)).to eq(user1)
+ expect(User.by_username_or_id('foo')).to eq(user1)
+ expect(User.by_username_or_id(-1)).to be_nil
+ expect(User.by_username_or_id('bar')).to be_nil
end
end
@@ -301,14 +307,24 @@ describe User do
end
end
+ describe ".clean_username" do
+
+ let!(:user) { create(:user, username: "johngitlab-etc") }
+ let!(:namespace) { create(:namespace, path: "JohnGitLab-etc1") }
+
+ it "cleans a username and makes sure it's available" do
+ expect(User.clean_username("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2")
+ end
+ end
+
describe 'all_ssh_keys' do
- it { should have_many(:keys).dependent(:destroy) }
+ it { is_expected.to have_many(:keys).dependent(:destroy) }
it "should have all ssh keys" do
user = create :user
key = create :key, key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD33bWLBxu48Sev9Fert1yzEO4WGcWglWF7K/AwblIUFselOt/QdOL9DSjpQGxLagO1s9wl53STIO8qGS4Ms0EJZyIXOEFMjFJ5xmjSy+S37By4sG7SsltQEHMxtbtFOaW5LV2wCrX+rUsRNqLMamZjgjcPO0/EgGCXIGMAYW4O7cwGZdXWYIhQ1Vwy+CsVMDdPkPgBXqK7nR/ey8KMs8ho5fMNgB5hBw/AL9fNGhRw3QTD6Q12Nkhl4VZES2EsZqlpNnJttnPdp847DUsT6yuLRlfiQfz5Cn9ysHFdXObMN5VYIiPFwHeYCZp1X2S4fDZooRE8uOLTfxWHPXwrhqSH", user_id: user.id
- user.all_ssh_keys.should include(key.key)
+ expect(user.all_ssh_keys).to include(key.key)
end
end
@@ -317,12 +333,12 @@ describe User do
it "should be true if avatar is image" do
user.update_attribute(:avatar, 'uploads/avatar.png')
- user.avatar_type.should be_true
+ expect(user.avatar_type).to be_truthy
end
it "should be false if avatar is html page" do
user.update_attribute(:avatar, 'uploads/avatar.html')
- user.avatar_type.should == ["only images allowed"]
+ expect(user.avatar_type).to eq(["only images allowed"])
end
end
@@ -333,7 +349,7 @@ describe User do
# Create a condition which would otherwise cause 'true' to be returned
user.stub(ldap_user?: true)
user.last_credential_check_at = nil
- expect(user.requires_ldap_check?).to be_false
+ expect(user.requires_ldap_check?).to be_falsey
end
context 'when LDAP is enabled' do
@@ -341,7 +357,7 @@ describe User do
it 'is false for non-LDAP users' do
user.stub(ldap_user?: false)
- expect(user.requires_ldap_check?).to be_false
+ expect(user.requires_ldap_check?).to be_falsey
end
context 'and when the user is an LDAP user' do
@@ -349,33 +365,38 @@ describe User do
it 'is true when the user has never had an LDAP check before' do
user.last_credential_check_at = nil
- expect(user.requires_ldap_check?).to be_true
+ expect(user.requires_ldap_check?).to be_truthy
end
it 'is true when the last LDAP check happened over 1 hour ago' do
user.last_credential_check_at = 2.hours.ago
- expect(user.requires_ldap_check?).to be_true
+ expect(user.requires_ldap_check?).to be_truthy
end
end
end
end
describe :ldap_user? do
- let(:user) { build(:user, :ldap) }
-
it "is true if provider name starts with ldap" do
- user.provider = 'ldapmain'
- expect( user.ldap_user? ).to be_true
+ user = create(:omniauth_user, provider: 'ldapmain')
+ expect( user.ldap_user? ).to be_truthy
end
it "is false for other providers" do
- user.provider = 'other-provider'
- expect( user.ldap_user? ).to be_false
+ user = create(:omniauth_user, provider: 'other-provider')
+ expect( user.ldap_user? ).to be_falsey
end
it "is false if no extern_uid is provided" do
- user.extern_uid = nil
- expect( user.ldap_user? ).to be_false
+ user = create(:omniauth_user, extern_uid: nil)
+ expect( user.ldap_user? ).to be_falsey
+ end
+ end
+
+ describe :ldap_identity do
+ it "returns ldap identity" do
+ user = create :omniauth_user
+ expect(user.ldap_identity.provider).not_to be_empty
end
end
@@ -429,24 +450,24 @@ describe User do
project1 = create :project, :public
project2 = create :project, :public
- expect(user.starred?(project1)).to be_false
- expect(user.starred?(project2)).to be_false
+ expect(user.starred?(project1)).to be_falsey
+ expect(user.starred?(project2)).to be_falsey
star1 = UsersStarProject.create!(project: project1, user: user)
- expect(user.starred?(project1)).to be_true
- expect(user.starred?(project2)).to be_false
+ expect(user.starred?(project1)).to be_truthy
+ expect(user.starred?(project2)).to be_falsey
star2 = UsersStarProject.create!(project: project2, user: user)
- expect(user.starred?(project1)).to be_true
- expect(user.starred?(project2)).to be_true
+ expect(user.starred?(project1)).to be_truthy
+ expect(user.starred?(project2)).to be_truthy
star1.destroy
- expect(user.starred?(project1)).to be_false
- expect(user.starred?(project2)).to be_true
+ expect(user.starred?(project1)).to be_falsey
+ expect(user.starred?(project2)).to be_truthy
star2.destroy
- expect(user.starred?(project1)).to be_false
- expect(user.starred?(project2)).to be_false
+ expect(user.starred?(project1)).to be_falsey
+ expect(user.starred?(project2)).to be_falsey
end
end
@@ -455,11 +476,11 @@ describe User do
user = create :user
project = create :project, :public
- expect(user.starred?(project)).to be_false
+ expect(user.starred?(project)).to be_falsey
user.toggle_star(project)
- expect(user.starred?(project)).to be_true
+ expect(user.starred?(project)).to be_truthy
user.toggle_star(project)
- expect(user.starred?(project)).to be_false
+ expect(user.starred?(project)).to be_falsey
end
end
@@ -469,25 +490,53 @@ describe User do
@user = create :user, created_at: Date.today, last_sign_in_at: Date.today, name: 'Alpha'
@user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega'
end
-
+
it "sorts users as recently_signed_in" do
- User.sort('recent_sign_in').first.should == @user
+ expect(User.sort('recent_sign_in').first).to eq(@user)
end
it "sorts users as late_signed_in" do
- User.sort('oldest_sign_in').first.should == @user1
+ expect(User.sort('oldest_sign_in').first).to eq(@user1)
end
it "sorts users as recently_created" do
- User.sort('recently_created').first.should == @user
+ expect(User.sort('created_desc').first).to eq(@user)
end
it "sorts users as late_created" do
- User.sort('late_created').first.should == @user1
+ expect(User.sort('created_asc').first).to eq(@user1)
end
it "sorts users by name when nil is passed" do
- User.sort(nil).first.should == @user
+ expect(User.sort(nil).first).to eq(@user)
+ end
+ end
+
+ describe "#contributed_projects_ids" do
+
+ subject { create(:user) }
+ let!(:project1) { create(:project) }
+ let!(:project2) { create(:project, forked_from_project: project3) }
+ let!(:project3) { create(:project) }
+ let!(:merge_request) { create(:merge_request, source_project: project2, target_project: project3, author: subject) }
+ let!(:push_event) { create(:event, action: Event::PUSHED, project: project1, target: project1, author: subject) }
+ let!(:merge_event) { create(:event, action: Event::CREATED, project: project3, target: merge_request, author: subject) }
+
+ before do
+ project1.team << [subject, :master]
+ project2.team << [subject, :master]
+ end
+
+ it "includes IDs for projects the user has pushed to" do
+ expect(subject.contributed_projects_ids).to include(project1.id)
+ end
+
+ it "includes IDs for projects the user has had merge requests merged into" do
+ expect(subject.contributed_projects_ids).to include(project3.id)
+ end
+
+ it "doesn't include IDs for unrelated projects" do
+ expect(subject.contributed_projects_ids).not_to include(project2.id)
end
end
end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index d065431ee3a1e6700af196389dea0e0922ace5ae..fceb7668cac4cf22dcb1b73d97b021ba2137057c 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -16,27 +16,27 @@ describe WikiPage do
end
it "sets the slug attribute" do
- @wiki_page.slug.should == "test-page"
+ expect(@wiki_page.slug).to eq("test-page")
end
it "sets the title attribute" do
- @wiki_page.title.should == "test page"
+ expect(@wiki_page.title).to eq("test page")
end
it "sets the formatted content attribute" do
- @wiki_page.content.should == "test content"
+ expect(@wiki_page.content).to eq("test content")
end
it "sets the format attribute" do
- @wiki_page.format.should == :markdown
+ expect(@wiki_page.format).to eq(:markdown)
end
it "sets the message attribute" do
- @wiki_page.message.should == "test commit"
+ expect(@wiki_page.message).to eq("test commit")
end
it "sets the version attribute" do
- @wiki_page.version.should be_a Grit::Commit
+ expect(@wiki_page.version).to be_a Gollum::Git::Commit
end
end
end
@@ -48,12 +48,12 @@ describe WikiPage do
it "validates presence of title" do
subject.attributes.delete(:title)
- subject.valid?.should be_false
+ expect(subject.valid?).to be_falsey
end
it "validates presence of content" do
subject.attributes.delete(:content)
- subject.valid?.should be_false
+ expect(subject.valid?).to be_falsey
end
end
@@ -69,11 +69,52 @@ describe WikiPage do
context "with valid attributes" do
it "saves the wiki page" do
subject.create(@wiki_attr)
- wiki.find_page("Index").should_not be_nil
+ expect(wiki.find_page("Index")).not_to be_nil
end
it "returns true" do
- subject.create(@wiki_attr).should == true
+ expect(subject.create(@wiki_attr)).to eq(true)
+ end
+ end
+ end
+
+ describe "dot in the title" do
+ let(:title) { 'Index v1.2.3' }
+
+ before do
+ @wiki_attr = {title: title, content: "Home Page", format: "markdown"}
+ end
+
+ describe "#create" do
+ after do
+ destroy_page(title)
+ end
+
+ context "with valid attributes" do
+ it "saves the wiki page" do
+ subject.create(@wiki_attr)
+ expect(wiki.find_page(title)).not_to be_nil
+ end
+
+ it "returns true" do
+ expect(subject.create(@wiki_attr)).to eq(true)
+ end
+ end
+ end
+
+ describe "#update" do
+ before do
+ create_page(title, "content")
+ @page = wiki.find_page(title)
+ end
+
+ it "updates the content of the page" do
+ @page.update("new content")
+ @page = wiki.find_page(title)
+ end
+
+ it "returns true" do
+ expect(@page.update("more content")).to be_truthy
end
end
end
@@ -95,7 +136,7 @@ describe WikiPage do
end
it "returns true" do
- @page.update("more content").should be_true
+ expect(@page.update("more content")).to be_truthy
end
end
end
@@ -108,11 +149,11 @@ describe WikiPage do
it "should delete the page" do
@page.delete
- wiki.pages.should be_empty
+ expect(wiki.pages).to be_empty
end
it "should return true" do
- @page.delete.should == true
+ expect(@page.delete).to eq(true)
end
end
@@ -128,7 +169,7 @@ describe WikiPage do
it "returns an array of all commits for the page" do
3.times { |i| @page.update("content #{i}") }
- @page.versions.count.should == 4
+ expect(@page.versions.count).to eq(4)
end
end
@@ -144,7 +185,7 @@ describe WikiPage do
it "should be replace a hyphen to a space" do
@page.title = "Import-existing-repositories-into-GitLab"
- @page.title.should == "Import existing repositories into GitLab"
+ expect(@page.title).to eq("Import existing repositories into GitLab")
end
end
diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb
index e2f222c0d341266ae63391ecc0a68277cce4d7ab..20cb30a39bb208cc11d05795b21fd57f6c687a48 100644
--- a/spec/requests/api/api_helpers_spec.rb
+++ b/spec/requests/api/api_helpers_spec.rb
@@ -41,32 +41,33 @@ describe API, api: true do
describe ".current_user" do
it "should return nil for an invalid token" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
- current_user.should be_nil
+ allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
+ expect(current_user).to be_nil
end
it "should return nil for a user without access" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
Gitlab::UserAccess.stub(allowed?: false)
- current_user.should be_nil
+ expect(current_user).to be_nil
end
it "should leave user as is when sudo not specified" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
- current_user.should == user
+ expect(current_user).to eq(user)
clear_env
params[API::APIHelpers::PRIVATE_TOKEN_PARAM] = user.private_token
- current_user.should == user
+ expect(current_user).to eq(user)
end
it "should change current user to sudo when admin" do
set_env(admin, user.id)
- current_user.should == user
+ expect(current_user).to eq(user)
set_param(admin, user.id)
- current_user.should == user
+ expect(current_user).to eq(user)
set_env(admin, user.username)
- current_user.should == user
+ expect(current_user).to eq(user)
set_param(admin, user.username)
- current_user.should == user
+ expect(current_user).to eq(user)
end
it "should throw an error when the current user is not an admin and attempting to sudo" do
@@ -82,8 +83,8 @@ describe API, api: true do
it "should throw an error when the user cannot be found for a given id" do
id = user.id + admin.id
- user.id.should_not == id
- admin.id.should_not == id
+ expect(user.id).not_to eq(id)
+ expect(admin.id).not_to eq(id)
set_env(admin, id)
expect { current_user }.to raise_error
@@ -93,8 +94,8 @@ describe API, api: true do
it "should throw an error when the user cannot be found for a given username" do
username = "#{user.username}#{admin.username}"
- user.username.should_not == username
- admin.username.should_not == username
+ expect(user.username).not_to eq(username)
+ expect(admin.username).not_to eq(username)
set_env(admin, username)
expect { current_user }.to raise_error
@@ -104,69 +105,69 @@ describe API, api: true do
it "should handle sudo's to oneself" do
set_env(admin, admin.id)
- current_user.should == admin
+ expect(current_user).to eq(admin)
set_param(admin, admin.id)
- current_user.should == admin
+ expect(current_user).to eq(admin)
set_env(admin, admin.username)
- current_user.should == admin
+ expect(current_user).to eq(admin)
set_param(admin, admin.username)
- current_user.should == admin
+ expect(current_user).to eq(admin)
end
it "should handle multiple sudo's to oneself" do
set_env(admin, user.id)
- current_user.should == user
- current_user.should == user
+ expect(current_user).to eq(user)
+ expect(current_user).to eq(user)
set_env(admin, user.username)
- current_user.should == user
- current_user.should == user
+ expect(current_user).to eq(user)
+ expect(current_user).to eq(user)
set_param(admin, user.id)
- current_user.should == user
- current_user.should == user
+ expect(current_user).to eq(user)
+ expect(current_user).to eq(user)
set_param(admin, user.username)
- current_user.should == user
- current_user.should == user
+ expect(current_user).to eq(user)
+ expect(current_user).to eq(user)
end
it "should handle multiple sudo's to oneself using string ids" do
set_env(admin, user.id.to_s)
- current_user.should == user
- current_user.should == user
+ expect(current_user).to eq(user)
+ expect(current_user).to eq(user)
set_param(admin, user.id.to_s)
- current_user.should == user
- current_user.should == user
+ expect(current_user).to eq(user)
+ expect(current_user).to eq(user)
end
end
describe '.sudo_identifier' do
it "should return integers when input is an int" do
set_env(admin, '123')
- sudo_identifier.should == 123
+ expect(sudo_identifier).to eq(123)
set_env(admin, '0001234567890')
- sudo_identifier.should == 1234567890
+ expect(sudo_identifier).to eq(1234567890)
set_param(admin, '123')
- sudo_identifier.should == 123
+ expect(sudo_identifier).to eq(123)
set_param(admin, '0001234567890')
- sudo_identifier.should == 1234567890
+ expect(sudo_identifier).to eq(1234567890)
end
it "should return string when input is an is not an int" do
set_env(admin, '12.30')
- sudo_identifier.should == "12.30"
+ expect(sudo_identifier).to eq("12.30")
set_env(admin, 'hello')
- sudo_identifier.should == 'hello'
+ expect(sudo_identifier).to eq('hello')
set_env(admin, ' 123')
- sudo_identifier.should == ' 123'
+ expect(sudo_identifier).to eq(' 123')
set_param(admin, '12.30')
- sudo_identifier.should == "12.30"
+ expect(sudo_identifier).to eq("12.30")
set_param(admin, 'hello')
- sudo_identifier.should == 'hello'
+ expect(sudo_identifier).to eq('hello')
set_param(admin, ' 123')
- sudo_identifier.should == ' 123'
+ expect(sudo_identifier).to eq(' 123')
end
end
end
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index b45572c39fd18995b37322c2e69cea37e107cc5e..f40d68b75a4ea2399760d6714e57f55b618d2e11 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -15,79 +15,79 @@ describe API::API, api: true do
describe "GET /projects/:id/repository/branches" do
it "should return an array of project branches" do
get api("/projects/#{project.id}/repository/branches", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['name'].should == project.repository.branch_names.first
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).to eq(project.repository.branch_names.first)
end
end
describe "GET /projects/:id/repository/branches/:branch" do
it "should return the branch information for a single branch" do
get api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['name'].should == branch_name
- json_response['commit']['id'].should == branch_sha
- json_response['protected'].should == false
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(false)
end
it "should return a 403 error if guest" do
get api("/projects/#{project.id}/repository/branches", user2)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it "should return a 404 error if branch is not available" do
get api("/projects/#{project.id}/repository/branches/unknown", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
describe "PUT /projects/:id/repository/branches/:branch/protect" do
it "should protect a single branch" do
put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['name'].should == branch_name
- json_response['commit']['id'].should == branch_sha
- json_response['protected'].should == true
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(true)
end
it "should return a 404 error if branch not found" do
put api("/projects/#{project.id}/repository/branches/unknown/protect", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should return a 403 error if guest" do
put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it "should return success when protect branch again" do
put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
end
describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
it "should unprotect a single branch" do
put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['name'].should == branch_name
- json_response['commit']['id'].should == branch_sha
- json_response['protected'].should == false
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(false)
end
it "should return success when unprotect branch" do
put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should return success when unprotect branch again" do
put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
end
@@ -97,46 +97,46 @@ describe API::API, api: true do
branch_name: 'feature1',
ref: branch_sha
- response.status.should == 201
+ expect(response.status).to eq(201)
- json_response['name'].should == 'feature1'
- json_response['commit']['id'].should == branch_sha
+ expect(json_response['name']).to eq('feature1')
+ expect(json_response['commit']['id']).to eq(branch_sha)
end
it "should deny for user without push access" do
post api("/projects/#{project.id}/repository/branches", user2),
branch_name: branch_name,
ref: branch_sha
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it 'should return 400 if branch name is invalid' do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new design',
ref: branch_sha
- response.status.should == 400
- json_response['message'].should == 'Branch name invalid'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Branch name invalid')
end
it 'should return 400 if branch already exists' do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design1',
ref: branch_sha
- response.status.should == 201
+ expect(response.status).to eq(201)
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design1',
ref: branch_sha
- response.status.should == 400
- json_response['message'].should == 'Branch already exists'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Branch already exists')
end
it 'should return 400 if ref name is invalid' do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design3',
ref: 'foo'
- response.status.should == 400
- json_response['message'].should == 'Invalid reference name'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Invalid reference name')
end
end
@@ -145,26 +145,26 @@ describe API::API, api: true do
it "should remove branch" do
delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
- response.status.should == 200
- json_response['branch_name'].should == branch_name
+ expect(response.status).to eq(200)
+ expect(json_response['branch_name']).to eq(branch_name)
end
it 'should return 404 if branch not exists' do
delete api("/projects/#{project.id}/repository/branches/foobar", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should remove protected branch" do
project.protected_branches.create(name: branch_name)
delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
- response.status.should == 405
- json_response['message'].should == 'Protected branch cant be removed'
+ expect(response.status).to eq(405)
+ expect(json_response['message']).to eq('Protected branch cant be removed')
end
it "should not remove HEAD branch" do
delete api("/projects/#{project.id}/repository/branches/master", user)
- response.status.should == 405
- json_response['message'].should == 'Cannot remove HEAD branch'
+ expect(response.status).to eq(405)
+ expect(json_response['message']).to eq('Cannot remove HEAD branch')
end
end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index a3f58f509132c9dd90b046fc5f55b5628faca54a..9ea60e1a4adb597c230dd5148df8a02dc93737a0 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -18,17 +18,17 @@ describe API::API, api: true do
it "should return project commits" do
get api("/projects/#{project.id}/repository/commits", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response.should be_an Array
- json_response.first['id'].should == project.repository.commit.id
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(project.repository.commit.id)
end
end
context "unauthorized user" do
it "should not return project commits" do
get api("/projects/#{project.id}/repository/commits")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
@@ -37,21 +37,21 @@ describe API::API, api: true do
context "authorized user" do
it "should return a commit by sha" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
- response.status.should == 200
- json_response['id'].should == project.repository.commit.id
- json_response['title'].should == project.repository.commit.title
+ expect(response.status).to eq(200)
+ expect(json_response['id']).to eq(project.repository.commit.id)
+ expect(json_response['title']).to eq(project.repository.commit.title)
end
it "should return a 404 error if not found" do
get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context "unauthorized user" do
it "should not return the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
@@ -62,23 +62,23 @@ describe API::API, api: true do
it "should return the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response.should be_an Array
- json_response.length.should >= 1
- json_response.first.keys.should include "diff"
+ expect(json_response).to be_an Array
+ expect(json_response.length).to be >= 1
+ expect(json_response.first.keys).to include "diff"
end
it "should return a 404 error if invalid commit" do
get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context "unauthorized user" do
it "should not return the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
@@ -87,23 +87,23 @@ describe API::API, api: true do
context 'authorized user' do
it 'should return merge_request comments' do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['note'].should == 'a comment on a commit'
- json_response.first['author']['id'].should == user.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['note']).to eq('a comment on a commit')
+ expect(json_response.first['author']['id']).to eq(user.id)
end
it 'should return a 404 error if merge_request_id not found' do
get api("/projects/#{project.id}/repository/commits/1234ab/comments", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context 'unauthorized user' do
it 'should not return the diff of the selected commit' do
get api("/projects/#{project.id}/repository/commits/1234ab/comments")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
@@ -112,37 +112,37 @@ describe API::API, api: true do
context 'authorized user' do
it 'should return comment' do
post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment'
- response.status.should == 201
- json_response['note'].should == 'My comment'
- json_response['path'].should be_nil
- json_response['line'].should be_nil
- json_response['line_type'].should be_nil
+ expect(response.status).to eq(201)
+ expect(json_response['note']).to eq('My comment')
+ expect(json_response['path']).to be_nil
+ expect(json_response['line']).to be_nil
+ expect(json_response['line_type']).to be_nil
end
it 'should return the inline comment' do
post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.diffs.first.new_path, line: 7, line_type: 'new'
- response.status.should == 201
- json_response['note'].should == 'My comment'
- json_response['path'].should == project.repository.commit.diffs.first.new_path
- json_response['line'].should == 7
- json_response['line_type'].should == 'new'
+ expect(response.status).to eq(201)
+ expect(json_response['note']).to eq('My comment')
+ expect(json_response['path']).to eq(project.repository.commit.diffs.first.new_path)
+ expect(json_response['line']).to eq(7)
+ expect(json_response['line_type']).to eq('new')
end
it 'should return 400 if note is missing' do
post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 404 if note is attached to non existent commit' do
post api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment'
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context 'unauthorized user' do
it 'should not return the diff of the selected commit' do
post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..39949a9042286cafc791a6554d79b5dadbf9223f
--- /dev/null
+++ b/spec/requests/api/doorkeeper_access_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+ include ApiHelpers
+
+ let!(:user) { create(:user) }
+ let!(:application) { Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "https://app.com", :owner => user) }
+ let!(:token) { Doorkeeper::AccessToken.create! :application_id => application.id, :resource_owner_id => user.id }
+
+
+ describe "when unauthenticated" do
+ it "returns authentication success" do
+ get api("/user"), :access_token => token.token
+ expect(response.status).to eq(200)
+ end
+ end
+
+ describe "when token invalid" do
+ it "returns authentication error" do
+ get api("/user"), :access_token => "123a"
+ expect(response.status).to eq(401)
+ end
+ end
+
+ describe "authorization by private token" do
+ it "returns authentication success" do
+ get api("/user", user)
+ expect(response.status).to eq(200)
+ end
+ end
+end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index b43a202aec03241caf75f1ef52190769d06b8818..bab8888a631277b9c79360405c3fa5bdf6c50ac2 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -16,15 +16,15 @@ describe API::API, api: true do
}
get api("/projects/#{project.id}/repository/files", user), params
- response.status.should == 200
- json_response['file_path'].should == file_path
- json_response['file_name'].should == 'popen.rb'
- Base64.decode64(json_response['content']).lines.first.should == "require 'fileutils'\n"
+ expect(response.status).to eq(200)
+ expect(json_response['file_path']).to eq(file_path)
+ expect(json_response['file_name']).to eq('popen.rb')
+ expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
end
it "should return a 400 bad request if no params given" do
get api("/projects/#{project.id}/repository/files", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 404 if such file does not exist" do
@@ -34,7 +34,7 @@ describe API::API, api: true do
}
get api("/projects/#{project.id}/repository/files", user), params
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -54,13 +54,13 @@ describe API::API, api: true do
)
post api("/projects/#{project.id}/repository/files", user), valid_params
- response.status.should == 201
- json_response['file_path'].should == 'newfile.rb'
+ expect(response.status).to eq(201)
+ expect(json_response['file_path']).to eq('newfile.rb')
end
it "should return a 400 bad request if no params given" do
post api("/projects/#{project.id}/repository/files", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 400 if satellite fails to create file" do
@@ -69,7 +69,7 @@ describe API::API, api: true do
)
post api("/projects/#{project.id}/repository/files", user), valid_params
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
@@ -89,22 +89,42 @@ describe API::API, api: true do
)
put api("/projects/#{project.id}/repository/files", user), valid_params
- response.status.should == 200
- json_response['file_path'].should == file_path
+ expect(response.status).to eq(200)
+ expect(json_response['file_path']).to eq(file_path)
end
it "should return a 400 bad request if no params given" do
put api("/projects/#{project.id}/repository/files", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
- it "should return a 400 if satellite fails to create file" do
- Gitlab::Satellite::EditFileAction.any_instance.stub(
- commit!: false,
- )
+ it 'should return a 400 if the checkout fails' do
+ Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
+ .and_raise(Gitlab::Satellite::CheckoutFailed)
+
+ put api("/projects/#{project.id}/repository/files", user), valid_params
+ expect(response.status).to eq(400)
+
+ ref = valid_params[:branch_name]
+ expect(response.body).to match("ref '#{ref}' could not be checked out")
+ end
+
+ it 'should return a 409 if the file was not modified' do
+ Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
+ .and_raise(Gitlab::Satellite::CommitFailed)
+
+ put api("/projects/#{project.id}/repository/files", user), valid_params
+ expect(response.status).to eq(409)
+ expect(response.body).to match("Maybe there was nothing to commit?")
+ end
+
+ it 'should return a 409 if the push fails' do
+ Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
+ .and_raise(Gitlab::Satellite::PushFailed)
put api("/projects/#{project.id}/repository/files", user), valid_params
- response.status.should == 400
+ expect(response.status).to eq(409)
+ expect(response.body).to match("Maybe the file was changed by another process?")
end
end
@@ -123,13 +143,13 @@ describe API::API, api: true do
)
delete api("/projects/#{project.id}/repository/files", user), valid_params
- response.status.should == 200
- json_response['file_path'].should == file_path
+ expect(response.status).to eq(200)
+ expect(json_response['file_path']).to eq(file_path)
end
it "should return a 400 bad request if no params given" do
delete api("/projects/#{project.id}/repository/files", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 400 if satellite fails to create file" do
@@ -138,7 +158,7 @@ describe API::API, api: true do
)
delete api("/projects/#{project.id}/repository/files", user), valid_params
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
end
diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb
index cbbd1e7de5a77beacd365cf6af77f83c16b2eae9..fb3ff552c8d412440c4b4b6263d748cecb08ec77 100644
--- a/spec/requests/api/fork_spec.rb
+++ b/spec/requests/api/fork_spec.rb
@@ -23,50 +23,50 @@ describe API::API, api: true do
context 'when authenticated' do
it 'should fork if user has sufficient access to project' do
post api("/projects/fork/#{project.id}", user2)
- response.status.should == 201
- json_response['name'].should == project.name
- json_response['path'].should == project.path
- json_response['owner']['id'].should == user2.id
- json_response['namespace']['id'].should == user2.namespace.id
- json_response['forked_from_project']['id'].should == project.id
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['path']).to eq(project.path)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['id']).to eq(user2.namespace.id)
+ expect(json_response['forked_from_project']['id']).to eq(project.id)
end
it 'should fork if user is admin' do
post api("/projects/fork/#{project.id}", admin)
- response.status.should == 201
- json_response['name'].should == project.name
- json_response['path'].should == project.path
- json_response['owner']['id'].should == admin.id
- json_response['namespace']['id'].should == admin.namespace.id
- json_response['forked_from_project']['id'].should == project.id
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['path']).to eq(project.path)
+ expect(json_response['owner']['id']).to eq(admin.id)
+ expect(json_response['namespace']['id']).to eq(admin.namespace.id)
+ expect(json_response['forked_from_project']['id']).to eq(project.id)
end
it 'should fail on missing project access for the project to fork' do
post api("/projects/fork/#{project.id}", user3)
- response.status.should == 404
- json_response['message'].should == '404 Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
end
it 'should fail if forked project exists in the user namespace' do
post api("/projects/fork/#{project.id}", user)
- response.status.should == 409
- json_response['message']['base'].should == ['Invalid fork destination']
- json_response['message']['name'].should == ['has already been taken']
- json_response['message']['path'].should == ['has already been taken']
+ expect(response.status).to eq(409)
+ expect(json_response['message']['base']).to eq(['Invalid fork destination'])
+ expect(json_response['message']['name']).to eq(['has already been taken'])
+ expect(json_response['message']['path']).to eq(['has already been taken'])
end
it 'should fail if project to fork from does not exist' do
post api('/projects/fork/424242', user)
- response.status.should == 404
- json_response['message'].should == '404 Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
end
end
context 'when unauthenticated' do
it 'should return authentication error' do
post api("/projects/fork/#{project.id}")
- response.status.should == 401
- json_response['message'].should == '401 Unauthorized'
+ expect(response.status).to eq(401)
+ expect(json_response['message']).to eq('401 Unauthorized')
end
end
end
diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb
index 4957186f6056d0b4e5269082a8ac537debbeb06c..8ba6876a95b112b5f66c28467699edca39f223bd 100644
--- a/spec/requests/api/group_members_spec.rb
+++ b/spec/requests/api/group_members_spec.rb
@@ -31,20 +31,20 @@ describe API::API, api: true do
it "each user: should return an array of members groups of group3" do
[owner, master, developer, reporter, guest].each do |user|
get api("/groups/#{group_with_members.id}/members", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.size.should == 5
- json_response.find { |e| e['id']==owner.id }['access_level'].should == GroupMember::OWNER
- json_response.find { |e| e['id']==reporter.id }['access_level'].should == GroupMember::REPORTER
- json_response.find { |e| e['id']==developer.id }['access_level'].should == GroupMember::DEVELOPER
- json_response.find { |e| e['id']==master.id }['access_level'].should == GroupMember::MASTER
- json_response.find { |e| e['id']==guest.id }['access_level'].should == GroupMember::GUEST
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(5)
+ expect(json_response.find { |e| e['id']==owner.id }['access_level']).to eq(GroupMember::OWNER)
+ expect(json_response.find { |e| e['id']==reporter.id }['access_level']).to eq(GroupMember::REPORTER)
+ expect(json_response.find { |e| e['id']==developer.id }['access_level']).to eq(GroupMember::DEVELOPER)
+ expect(json_response.find { |e| e['id']==master.id }['access_level']).to eq(GroupMember::MASTER)
+ expect(json_response.find { |e| e['id']==guest.id }['access_level']).to eq(GroupMember::GUEST)
end
end
it "users not part of the group should get access error" do
get api("/groups/#{group_with_members.id}/members", stranger)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
end
@@ -53,7 +53,7 @@ describe API::API, api: true do
context "when not a member of the group" do
it "should not add guest as member of group_no_members when adding being done by person outside the group" do
post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
@@ -66,9 +66,9 @@ describe API::API, api: true do
user_id: new_user.id, access_level: GroupMember::MASTER
}.to change { group_no_members.members.count }.by(1)
- response.status.should == 201
- json_response['name'].should == new_user.name
- json_response['access_level'].should == GroupMember::MASTER
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq(new_user.name)
+ expect(json_response['access_level']).to eq(GroupMember::MASTER)
end
it "should not allow guest to modify group members" do
@@ -79,27 +79,90 @@ describe API::API, api: true do
user_id: new_user.id, access_level: GroupMember::MASTER
}.not_to change { group_with_members.members.count }
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it "should return error if member already exists" do
post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER
- response.status.should == 409
+ expect(response.status).to eq(409)
end
it "should return a 400 error when user id is not given" do
post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 400 error when access level is not given" do
post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 422 error when access level is not known" do
post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234
- response.status.should == 422
+ expect(response.status).to eq(422)
+ end
+ end
+ end
+
+ describe 'PUT /groups/:id/members/:user_id' do
+ context 'when not a member of the group' do
+ it 'should return a 409 error if the user is not a group member' do
+ put(
+ api("/groups/#{group_no_members.id}/members/#{developer.id}",
+ owner), access_level: GroupMember::MASTER
+ )
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when a member of the group' do
+ it 'should return ok and update member access level' do
+ put(
+ api("/groups/#{group_with_members.id}/members/#{reporter.id}",
+ owner),
+ access_level: GroupMember::MASTER
+ )
+
+ expect(response.status).to eq(200)
+
+ get api("/groups/#{group_with_members.id}/members", owner)
+ json_reporter = json_response.find do |e|
+ e['id'] == reporter.id
+ end
+
+ expect(json_reporter['access_level']).to eq(GroupMember::MASTER)
+ end
+
+ it 'should not allow guest to modify group members' do
+ put(
+ api("/groups/#{group_with_members.id}/members/#{developer.id}",
+ guest),
+ access_level: GroupMember::MASTER
+ )
+
+ expect(response.status).to eq(403)
+
+ get api("/groups/#{group_with_members.id}/members", owner)
+ json_developer = json_response.find do |e|
+ e['id'] == developer.id
+ end
+
+ expect(json_developer['access_level']).to eq(GroupMember::DEVELOPER)
+ end
+
+ it 'should return a 400 error when access level is not given' do
+ put(
+ api("/groups/#{group_with_members.id}/members/#{master.id}", owner)
+ )
+ expect(response.status).to eq(400)
+ end
+
+ it 'should return a 422 error when access level is not known' do
+ put(
+ api("/groups/#{group_with_members.id}/members/#{master.id}", owner),
+ access_level: 1234
+ )
+ expect(response.status).to eq(422)
end
end
end
@@ -109,7 +172,7 @@ describe API::API, api: true do
it "should not delete guest's membership of group_with_members" do
random_user = create(:user)
delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
@@ -119,17 +182,17 @@ describe API::API, api: true do
delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner)
}.to change { group_with_members.members.count }.by(-1)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return a 404 error when user id is not known" do
delete api("/groups/#{group_with_members.id}/members/1328", owner)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should not allow guest to modify group members" do
delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 8dfd2cd650e79965f2ebf24c388aada08553d8da..d963dbac9f1196b285bfd57d3cd3539f89749fe3 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -18,26 +18,26 @@ describe API::API, api: true do
context "when unauthenticated" do
it "should return authentication error" do
get api("/groups")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when authenticated as user" do
it "normal user: should return an array of groups of user1" do
get api("/groups", user1)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['name'].should == group1.name
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(group1.name)
end
end
context "when authenticated as admin" do
it "admin: should return an array of all groups" do
get api("/groups", admin)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 2
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
end
end
end
@@ -46,31 +46,49 @@ describe API::API, api: true do
context "when authenticated as user" do
it "should return one of user1's groups" do
get api("/groups/#{group1.id}", user1)
- response.status.should == 200
+ expect(response.status).to eq(200)
json_response['name'] == group1.name
end
it "should not return a non existing group" do
get api("/groups/1328", user1)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should not return a group not attached to user1" do
get api("/groups/#{group2.id}", user1)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
context "when authenticated as admin" do
it "should return any existing group" do
get api("/groups/#{group2.id}", admin)
- response.status.should == 200
+ expect(response.status).to eq(200)
json_response['name'] == group2.name
end
it "should not return a non existing group" do
get api("/groups/1328", admin)
- response.status.should == 404
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when using group path in URL' do
+ it 'should return any existing group' do
+ get api("/groups/#{group1.path}", admin)
+ expect(response.status).to eq(200)
+ json_response['name'] == group2.name
+ end
+
+ it 'should not return a non existing group' do
+ get api('/groups/unknown', admin)
+ expect(response.status).to eq(404)
+ end
+
+ it 'should not return a group not attached to user1' do
+ get api("/groups/#{group2.path}", user1)
+ expect(response.status).to eq(403)
end
end
end
@@ -79,29 +97,30 @@ describe API::API, api: true do
context "when authenticated as user" do
it "should not create group" do
post api("/groups", user1), attributes_for(:group)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
context "when authenticated as admin" do
it "should create group" do
post api("/groups", admin), attributes_for(:group)
- response.status.should == 201
+ expect(response.status).to eq(201)
end
it "should not create group, duplicate" do
post api("/groups", admin), {name: "Duplicate Test", path: group2.path}
- response.status.should == 404
+ expect(response.status).to eq(400)
+ expect(response.message).to eq("Bad Request")
end
it "should return 400 bad request error if name not given" do
post api("/groups", admin), {path: group2.path}
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 400 bad request error if path not given" do
post api("/groups", admin), { name: 'test' }
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
end
@@ -110,36 +129,36 @@ describe API::API, api: true do
context "when authenticated as user" do
it "should remove group" do
delete api("/groups/#{group1.id}", user1)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should not remove a group if not an owner" do
user3 = create(:user)
group1.add_user(user3, Gitlab::Access::MASTER)
delete api("/groups/#{group1.id}", user3)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it "should not remove a non existing group" do
delete api("/groups/1328", user1)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should not remove a group not attached to user1" do
delete api("/groups/#{group2.id}", user1)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
context "when authenticated as admin" do
it "should remove any existing group" do
delete api("/groups/#{group2.id}", admin)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should not remove a non existing group" do
delete api("/groups/1328", admin)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
end
@@ -148,20 +167,20 @@ describe API::API, api: true do
let(:project) { create(:project) }
before(:each) do
Projects::TransferService.any_instance.stub(execute: true)
- Project.stub(:find).and_return(project)
+ allow(Project).to receive(:find).and_return(project)
end
context "when authenticated as user" do
it "should not transfer project to group" do
post api("/groups/#{group1.id}/projects/#{project.id}", user2)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
context "when authenticated as admin" do
it "should transfer project to group" do
post api("/groups/#{group1.id}/projects/#{project.id}", admin)
- response.status.should == 201
+ expect(response.status).to eq(201)
end
end
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 677b14940414b75d501b6e24b81ebf2b9fe5d618..4c7d15d6594e8766b7dbb701b4d39a808190ec35 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -11,8 +11,30 @@ describe API::API, api: true do
it do
get api("/internal/check"), secret_token: secret_token
- response.status.should == 200
- json_response['api_version'].should == API::API.version
+ expect(response.status).to eq(200)
+ expect(json_response['api_version']).to eq(API::API.version)
+ end
+ end
+
+ describe "GET /internal/broadcast_message" do
+ context "broadcast message exists" do
+ let!(:broadcast_message) { create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow ) }
+
+ it do
+ get api("/internal/broadcast_message"), secret_token: secret_token
+
+ expect(response.status).to eq(200)
+ expect(json_response["message"]).to eq(broadcast_message.message)
+ end
+ end
+
+ context "broadcast message doesn't exist" do
+ it do
+ get api("/internal/broadcast_message"), secret_token: secret_token
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_empty
+ end
end
end
@@ -20,13 +42,13 @@ describe API::API, api: true do
it do
get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['name'].should == user.name
+ expect(json_response['name']).to eq(user.name)
end
end
- describe "GET /internal/allowed" do
+ describe "POST /internal/allowed" do
context "access granted" do
before do
project.team << [user, :developer]
@@ -36,8 +58,8 @@ describe API::API, api: true do
it do
pull(key, project)
- response.status.should == 200
- response.body.should == 'true'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_truthy
end
end
@@ -45,8 +67,8 @@ describe API::API, api: true do
it do
push(key, project)
- response.status.should == 200
- response.body.should == 'true'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_truthy
end
end
end
@@ -60,8 +82,8 @@ describe API::API, api: true do
it do
pull(key, project)
- response.status.should == 200
- response.body.should == 'false'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
end
end
@@ -69,8 +91,8 @@ describe API::API, api: true do
it do
push(key, project)
- response.status.should == 200
- response.body.should == 'false'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
end
end
end
@@ -86,8 +108,8 @@ describe API::API, api: true do
it do
pull(key, personal_project)
- response.status.should == 200
- response.body.should == 'false'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
end
end
@@ -95,8 +117,8 @@ describe API::API, api: true do
it do
push(key, personal_project)
- response.status.should == 200
- response.body.should == 'false'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
end
end
end
@@ -113,8 +135,8 @@ describe API::API, api: true do
it do
pull(key, project)
- response.status.should == 200
- response.body.should == 'true'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_truthy
end
end
@@ -122,8 +144,8 @@ describe API::API, api: true do
it do
push(key, project)
- response.status.should == 200
- response.body.should == 'false'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
end
end
end
@@ -139,8 +161,8 @@ describe API::API, api: true do
it do
archive(key, project)
- response.status.should == 200
- response.body.should == 'true'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_truthy
end
end
@@ -148,11 +170,29 @@ describe API::API, api: true do
it do
archive(key, project)
- response.status.should == 200
- response.body.should == 'false'
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
end
end
end
+
+ context 'project does not exist' do
+ it do
+ pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists'))
+
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
+ end
+ end
+
+ context 'user does not exist' do
+ it do
+ pull(OpenStruct.new(id: 0), project)
+
+ expect(response.status).to eq(200)
+ expect(json_response["status"]).to be_falsey
+ end
+ end
end
def pull(key, project)
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 775d7b4e18d032cdd3f09d3a3b5b5ca7f1f0d3d3..b6b0427debf93666406cf5180af659bf6af273be 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -34,86 +34,87 @@ describe API::API, api: true do
context "when unauthenticated" do
it "should return authentication error" do
get api("/issues")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when authenticated" do
it "should return an array of issues" do
get api("/issues", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['title'].should == issue.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(issue.title)
end
it "should add pagination headers" do
get api("/issues?per_page=3", user)
- response.headers['Link'].should ==
+ expect(response.headers['Link']).to eq(
'; rel="first", ; rel="last"'
+ )
end
it 'should return an array of closed issues' do
get api('/issues?state=closed', user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['id'].should == closed_issue.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(closed_issue.id)
end
it 'should return an array of opened issues' do
get api('/issues?state=opened', user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['id'].should == issue.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(issue.id)
end
it 'should return an array of all issues' do
get api('/issues?state=all', user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 2
- json_response.first['id'].should == issue.id
- json_response.second['id'].should == closed_issue.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ expect(json_response.first['id']).to eq(issue.id)
+ expect(json_response.second['id']).to eq(closed_issue.id)
end
it 'should return an array of labeled issues' do
get api("/issues?labels=#{label.title}", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['labels'].should == [label.title]
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label.title])
end
it 'should return an array of labeled issues when at least one label matches' do
get api("/issues?labels=#{label.title},foo,bar", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['labels'].should == [label.title]
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label.title])
end
it 'should return an empty array if no issue matches labels' do
get api('/issues?labels=foo,bar', user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 0
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
end
it 'should return an array of labeled issues matching given state' do
get api("/issues?labels=#{label.title}&state=opened", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['labels'].should == [label.title]
- json_response.first['state'].should == 'opened'
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label.title])
+ expect(json_response.first['state']).to eq('opened')
end
it 'should return an empty array if no issue matches labels and state filters' do
get api("/issues?labels=#{label.title}&state=closed", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 0
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
end
end
end
@@ -124,78 +125,78 @@ describe API::API, api: true do
it "should return project issues" do
get api("#{base_url}/issues", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['title'].should == issue.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(issue.title)
end
it 'should return an array of labeled project issues' do
get api("#{base_url}/issues?labels=#{label.title}", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['labels'].should == [label.title]
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label.title])
end
it 'should return an array of labeled project issues when at least one label matches' do
get api("#{base_url}/issues?labels=#{label.title},foo,bar", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['labels'].should == [label.title]
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label.title])
end
it 'should return an empty array if no project issue matches labels' do
get api("#{base_url}/issues?labels=foo,bar", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 0
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
end
it 'should return an empty array if no issue matches milestone' do
get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 0
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
end
it 'should return an empty array if milestone does not exist' do
get api("#{base_url}/issues?milestone=foo", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 0
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
end
it 'should return an array of issues in given milestone' do
get api("#{base_url}/issues?milestone=#{title}", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 2
- json_response.first['id'].should == issue.id
- json_response.second['id'].should == closed_issue.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ expect(json_response.first['id']).to eq(issue.id)
+ expect(json_response.second['id']).to eq(closed_issue.id)
end
it 'should return an array of issues matching state in milestone' do
get api("#{base_url}/issues?milestone=#{milestone.title}"\
'&state=closed', user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['id'].should == closed_issue.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(closed_issue.id)
end
end
describe "GET /projects/:id/issues/:issue_id" do
it "should return a project issue by id" do
get api("/projects/#{project.id}/issues/#{issue.id}", user)
- response.status.should == 200
- json_response['title'].should == issue.title
- json_response['iid'].should == issue.iid
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq(issue.title)
+ expect(json_response['iid']).to eq(issue.iid)
end
it "should return 404 if issue id not found" do
get api("/projects/#{project.id}/issues/54321", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -203,32 +204,32 @@ describe API::API, api: true do
it "should create a new project issue" do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', labels: 'label, label2'
- response.status.should == 201
- json_response['title'].should == 'new issue'
- json_response['description'].should be_nil
- json_response['labels'].should == ['label', 'label2']
+ expect(response.status).to eq(201)
+ expect(json_response['title']).to eq('new issue')
+ expect(json_response['description']).to be_nil
+ expect(json_response['labels']).to eq(['label', 'label2'])
end
it "should return a 400 bad request if title not given" do
post api("/projects/#{project.id}/issues", user), labels: 'label, label2'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 400 on invalid label names' do
post api("/projects/#{project.id}/issues", user),
title: 'new issue',
labels: 'label, ?'
- response.status.should == 400
- json_response['message']['labels']['?']['title'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
end
it 'should return 400 if title is too long' do
post api("/projects/#{project.id}/issues", user),
title: 'g' * 256
- response.status.should == 400
- json_response['message']['title'].should == [
+ expect(response.status).to eq(400)
+ expect(json_response['message']['title']).to eq([
'is too long (maximum is 255 characters)'
- ]
+ ])
end
end
@@ -236,23 +237,23 @@ describe API::API, api: true do
it "should update a project issue" do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
title: 'updated title'
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['title'].should == 'updated title'
+ expect(json_response['title']).to eq('updated title')
end
it "should return 404 error if issue id not found" do
put api("/projects/#{project.id}/issues/44444", user),
title: 'updated title'
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it 'should return 400 on invalid label names' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
title: 'updated title',
labels: 'label, ?'
- response.status.should == 400
- json_response['message']['labels']['?']['title'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
end
end
@@ -263,49 +264,49 @@ describe API::API, api: true do
it 'should not update labels if not present' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
title: 'updated title'
- response.status.should == 200
- json_response['labels'].should == [label.title]
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to eq([label.title])
end
it 'should remove all labels' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: ''
- response.status.should == 200
- json_response['labels'].should == []
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to eq([])
end
it 'should update labels' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: 'foo,bar'
- response.status.should == 200
- json_response['labels'].should include 'foo'
- json_response['labels'].should include 'bar'
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to include 'foo'
+ expect(json_response['labels']).to include 'bar'
end
it 'should return 400 on invalid label names' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: 'label, ?'
- response.status.should == 400
- json_response['message']['labels']['?']['title'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
end
it 'should allow special label names' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: 'label:foo, label-bar,label_bar,label/bar'
- response.status.should == 200
- json_response['labels'].should include 'label:foo'
- json_response['labels'].should include 'label-bar'
- json_response['labels'].should include 'label_bar'
- json_response['labels'].should include 'label/bar'
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to include 'label:foo'
+ expect(json_response['labels']).to include 'label-bar'
+ expect(json_response['labels']).to include 'label_bar'
+ expect(json_response['labels']).to include 'label/bar'
end
it 'should return 400 if title is too long' do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
title: 'g' * 256
- response.status.should == 400
- json_response['message']['title'].should == [
+ expect(response.status).to eq(400)
+ expect(json_response['message']['title']).to eq([
'is too long (maximum is 255 characters)'
- ]
+ ])
end
end
@@ -313,17 +314,17 @@ describe API::API, api: true do
it "should update a project issue" do
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: 'label2', state_event: "close"
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['labels'].should include 'label2'
- json_response['state'].should eq "closed"
+ expect(json_response['labels']).to include 'label2'
+ expect(json_response['state']).to eq "closed"
end
end
describe "DELETE /projects/:id/issues/:issue_id" do
it "should delete a project issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", user)
- response.status.should == 405
+ expect(response.status).to eq(405)
end
end
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index dbddc8a7da40438390a29ebd1f1a958c60aca1e9..aff109a942469fe8d37b61ec2ed8a5a2ed0034b9 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -15,10 +15,10 @@ describe API::API, api: true do
describe 'GET /projects/:id/labels' do
it 'should return project labels' do
get api("/projects/#{project.id}/labels", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.size.should == 1
- json_response.first['name'].should == label1.name
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['name']).to eq(label1.name)
end
end
@@ -27,69 +27,69 @@ describe API::API, api: true do
post api("/projects/#{project.id}/labels", user),
name: 'Foo',
color: '#FFAABB'
- response.status.should == 201
- json_response['name'].should == 'Foo'
- json_response['color'].should == '#FFAABB'
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq('Foo')
+ expect(json_response['color']).to eq('#FFAABB')
end
it 'should return a 400 bad request if name not given' do
post api("/projects/#{project.id}/labels", user), color: '#FFAABB'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return a 400 bad request if color not given' do
post api("/projects/#{project.id}/labels", user), name: 'Foobar'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 400 for invalid color' do
post api("/projects/#{project.id}/labels", user),
name: 'Foo',
color: '#FFAA'
- response.status.should == 400
- json_response['message']['color'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['color']).to eq(['is invalid'])
end
it 'should return 400 for too long color code' do
post api("/projects/#{project.id}/labels", user),
name: 'Foo',
color: '#FFAAFFFF'
- response.status.should == 400
- json_response['message']['color'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['color']).to eq(['is invalid'])
end
it 'should return 400 for invalid name' do
post api("/projects/#{project.id}/labels", user),
name: '?',
color: '#FFAABB'
- response.status.should == 400
- json_response['message']['title'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['title']).to eq(['is invalid'])
end
it 'should return 409 if label already exists' do
post api("/projects/#{project.id}/labels", user),
name: 'label1',
color: '#FFAABB'
- response.status.should == 409
- json_response['message'].should == 'Label already exists'
+ expect(response.status).to eq(409)
+ expect(json_response['message']).to eq('Label already exists')
end
end
describe 'DELETE /projects/:id/labels' do
it 'should return 200 for existing label' do
delete api("/projects/#{project.id}/labels", user), name: 'label1'
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it 'should return 404 for non existing label' do
delete api("/projects/#{project.id}/labels", user), name: 'label2'
- response.status.should == 404
- json_response['message'].should == '404 Label Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Label Not Found')
end
it 'should return 400 for wrong parameters' do
delete api("/projects/#{project.id}/labels", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
@@ -99,47 +99,47 @@ describe API::API, api: true do
name: 'label1',
new_name: 'New Label',
color: '#FFFFFF'
- response.status.should == 200
- json_response['name'].should == 'New Label'
- json_response['color'].should == '#FFFFFF'
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq('New Label')
+ expect(json_response['color']).to eq('#FFFFFF')
end
it 'should return 200 if name is changed' do
put api("/projects/#{project.id}/labels", user),
name: 'label1',
new_name: 'New Label'
- response.status.should == 200
- json_response['name'].should == 'New Label'
- json_response['color'].should == label1.color
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq('New Label')
+ expect(json_response['color']).to eq(label1.color)
end
it 'should return 200 if colors is changed' do
put api("/projects/#{project.id}/labels", user),
name: 'label1',
color: '#FFFFFF'
- response.status.should == 200
- json_response['name'].should == label1.name
- json_response['color'].should == '#FFFFFF'
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(label1.name)
+ expect(json_response['color']).to eq('#FFFFFF')
end
it 'should return 404 if label does not exist' do
put api("/projects/#{project.id}/labels", user),
name: 'label2',
new_name: 'label3'
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it 'should return 400 if no label name given' do
put api("/projects/#{project.id}/labels", user), new_name: 'label2'
- response.status.should == 400
- json_response['message'].should == '400 (Bad request) "name" not given'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('400 (Bad request) "name" not given')
end
it 'should return 400 if no new parameters given' do
put api("/projects/#{project.id}/labels", user), name: 'label1'
- response.status.should == 400
- json_response['message'].should == 'Required parameters '\
- '"new_name" or "color" missing'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Required parameters '\
+ '"new_name" or "color" missing')
end
it 'should return 400 for invalid name' do
@@ -147,24 +147,24 @@ describe API::API, api: true do
name: 'label1',
new_name: '?',
color: '#FFFFFF'
- response.status.should == 400
- json_response['message']['title'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['title']).to eq(['is invalid'])
end
it 'should return 400 for invalid name' do
put api("/projects/#{project.id}/labels", user),
name: 'label1',
color: '#FF'
- response.status.should == 400
- json_response['message']['color'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['color']).to eq(['is invalid'])
end
it 'should return 400 for too long color code' do
post api("/projects/#{project.id}/labels", user),
name: 'Foo',
color: '#FFAAFFFF'
- response.status.should == 400
- json_response['message']['color'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['color']).to eq(['is invalid'])
end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 5ba3a33099154e4d6c917489103b9f96d9c91e54..9e252441a4f417d4aeeeb280161fac89f2cf0e08 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -16,46 +16,50 @@ describe API::API, api: true do
context "when unauthenticated" do
it "should return authentication error" do
get api("/projects/#{project.id}/merge_requests")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when authenticated" do
it "should return an array of all merge_requests" do
get api("/projects/#{project.id}/merge_requests", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 3
- json_response.first['title'].should == merge_request.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.last['title']).to eq(merge_request.title)
end
+
it "should return an array of all merge_requests" do
get api("/projects/#{project.id}/merge_requests?state", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 3
- json_response.first['title'].should == merge_request.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.last['title']).to eq(merge_request.title)
end
+
it "should return an array of open merge_requests" do
get api("/projects/#{project.id}/merge_requests?state=opened", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['title'].should == merge_request.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.last['title']).to eq(merge_request.title)
end
+
it "should return an array of closed merge_requests" do
get api("/projects/#{project.id}/merge_requests?state=closed", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 2
- json_response.first['title'].should == merge_request_closed.title
- json_response.second['title'].should == merge_request_merged.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ expect(json_response.second['title']).to eq(merge_request_closed.title)
+ expect(json_response.first['title']).to eq(merge_request_merged.title)
end
+
it "should return an array of merged merge_requests" do
get api("/projects/#{project.id}/merge_requests?state=merged", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['title'].should == merge_request_merged.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['title']).to eq(merge_request_merged.title)
end
context "with ordering" do
@@ -66,35 +70,38 @@ describe API::API, api: true do
it "should return an array of merge_requests in ascending order" do
get api("/projects/#{project.id}/merge_requests?sort=asc", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 3
- json_response.first['id'].should == @mr_earlier.id
- json_response.last['id'].should == @mr_later.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.last['id']).to eq(@mr_earlier.id)
+ expect(json_response.first['id']).to eq(@mr_later.id)
end
+
it "should return an array of merge_requests in descending order" do
get api("/projects/#{project.id}/merge_requests?sort=desc", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 3
- json_response.first['id'].should == @mr_later.id
- json_response.last['id'].should == @mr_earlier.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.first['id']).to eq(@mr_later.id)
+ expect(json_response.last['id']).to eq(@mr_earlier.id)
end
+
it "should return an array of merge_requests ordered by updated_at" do
get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 3
- json_response.first['id'].should == @mr_earlier.id
- json_response.last['id'].should == @mr_later.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.last['id']).to eq(@mr_earlier.id)
+ expect(json_response.first['id']).to eq(@mr_later.id)
end
+
it "should return an array of merge_requests ordered by created_at" do
get api("/projects/#{project.id}/merge_requests?sort=created_at", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 3
- json_response.first['id'].should == @mr_earlier.id
- json_response.last['id'].should == @mr_later.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(3)
+ expect(json_response.last['id']).to eq(@mr_earlier.id)
+ expect(json_response.first['id']).to eq(@mr_later.id)
end
end
end
@@ -103,14 +110,27 @@ describe API::API, api: true do
describe "GET /projects/:id/merge_request/:merge_request_id" do
it "should return merge_request" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user)
- response.status.should == 200
- json_response['title'].should == merge_request.title
- json_response['iid'].should == merge_request.iid
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq(merge_request.title)
+ expect(json_response['iid']).to eq(merge_request.iid)
end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
+ end
+ end
+
+ describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do
+ it 'should return the change information of the merge_request' do
+ get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user)
+ expect(response.status).to eq 200
+ expect(json_response['changes'].size).to eq(merge_request.diffs.size)
+ end
+
+ it 'returns a 404 when merge_request_id not found' do
+ get api("/projects/#{project.id}/merge_request/999/changes", user)
+ expect(response.status).to eq(404)
end
end
@@ -123,33 +143,33 @@ describe API::API, api: true do
target_branch: 'master',
author: user,
labels: 'label, label2'
- response.status.should == 201
- json_response['title'].should == 'Test merge_request'
- json_response['labels'].should == ['label', 'label2']
+ expect(response.status).to eq(201)
+ expect(json_response['title']).to eq('Test merge_request')
+ expect(json_response['labels']).to eq(['label', 'label2'])
end
it "should return 422 when source_branch equals target_branch" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
- response.status.should == 422
+ expect(response.status).to eq(422)
end
it "should return 400 when source_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", target_branch: "master", author: user
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 400 when target_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "stable", author: user
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 400 when title is missing" do
post api("/projects/#{project.id}/merge_requests", user),
target_branch: 'master', source_branch: 'stable'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 400 on invalid label names' do
@@ -159,9 +179,10 @@ describe API::API, api: true do
target_branch: 'master',
author: user,
labels: 'label, ?'
- response.status.should == 400
- json_response['message']['labels']['?']['title'].should ==
+ expect(response.status).to eq(400)
+ expect(json_response['message']['labels']['?']['title']).to eq(
['is invalid']
+ )
end
context 'with existing MR' do
@@ -182,7 +203,7 @@ describe API::API, api: true do
target_branch: 'master',
author: user
end.to change { MergeRequest.count }.by(0)
- response.status.should == 409
+ expect(response.status).to eq(409)
end
end
end
@@ -199,37 +220,37 @@ describe API::API, api: true do
it "should return merge_request" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
- response.status.should == 201
- json_response['title'].should == 'Test merge_request'
- json_response['description'].should == 'Test description for Test merge_request'
+ expect(response.status).to eq(201)
+ expect(json_response['title']).to eq('Test merge_request')
+ expect(json_response['description']).to eq('Test description for Test merge_request')
end
it "should not return 422 when source_branch equals target_branch" do
- project.id.should_not == fork_project.id
- fork_project.forked?.should be_true
- fork_project.forked_from_project.should == project
+ expect(project.id).not_to eq(fork_project.id)
+ expect(fork_project.forked?).to be_truthy
+ expect(fork_project.forked_from_project).to eq(project)
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id
- response.status.should == 201
- json_response['title'].should == 'Test merge_request'
+ expect(response.status).to eq(201)
+ expect(json_response['title']).to eq('Test merge_request')
end
it "should return 400 when source_branch is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 400 when target_branch is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 400 when title is missing" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id
- response.status.should == 400
+ expect(response.status).to eq(400)
end
context 'when target_branch is specified' do
@@ -240,7 +261,7 @@ describe API::API, api: true do
source_branch: 'stable',
author: user,
target_project_id: fork_project.id
- response.status.should == 422
+ expect(response.status).to eq(422)
end
it 'should return 422 if targeting a different fork' do
@@ -250,14 +271,14 @@ describe API::API, api: true do
source_branch: 'stable',
author: user2,
target_project_id: unrelated_project.id
- response.status.should == 422
+ expect(response.status).to eq(422)
end
end
it "should return 201 when target_branch is specified and for the same project" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id
- response.status.should == 201
+ expect(response.status).to eq(201)
end
end
end
@@ -265,8 +286,8 @@ describe API::API, api: true do
describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close"
- response.status.should == 200
- json_response['state'].should == 'closed'
+ expect(response.status).to eq(200)
+ expect(json_response['state']).to eq('closed')
end
end
@@ -274,55 +295,55 @@ describe API::API, api: true do
it "should return merge_request in case of success" do
MergeRequest.any_instance.stub(can_be_merged?: true, automerge!: true)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return 405 if branch can't be merged" do
MergeRequest.any_instance.stub(can_be_merged?: false)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
- response.status.should == 405
- json_response['message'].should == 'Branch cannot be merged'
+ expect(response.status).to eq(405)
+ expect(json_response['message']).to eq('Branch cannot be merged')
end
it "should return 405 if merge_request is not open" do
merge_request.close
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
- response.status.should == 405
- json_response['message'].should == '405 Method Not Allowed'
+ expect(response.status).to eq(405)
+ expect(json_response['message']).to eq('405 Method Not Allowed')
end
it "should return 401 if user has no permissions to merge" do
user2 = create(:user)
project.team << [user2, :reporter]
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user2)
- response.status.should == 401
- json_response['message'].should == '401 Unauthorized'
+ expect(response.status).to eq(401)
+ expect(json_response['message']).to eq('401 Unauthorized')
end
end
describe "PUT /projects/:id/merge_request/:merge_request_id" do
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title"
- response.status.should == 200
- json_response['title'].should == 'New title'
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq('New title')
end
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description"
- response.status.should == 200
- json_response['description'].should == 'New description'
+ expect(response.status).to eq(200)
+ expect(json_response['description']).to eq('New description')
end
it "should return 422 when source_branch and target_branch are renamed the same" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
- response.status.should == 422
+ expect(response.status).to eq(422)
end
it "should return merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki"
- response.status.should == 200
- json_response['target_branch'].should == 'wiki'
+ expect(response.status).to eq(200)
+ expect(json_response['target_branch']).to eq('wiki')
end
it 'should return 400 on invalid label names' do
@@ -330,43 +351,43 @@ describe API::API, api: true do
user),
title: 'new issue',
labels: 'label, ?'
- response.status.should == 400
- json_response['message']['labels']['?']['title'].should == ['is invalid']
+ expect(response.status).to eq(400)
+ expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
end
end
describe "POST /projects/:id/merge_request/:merge_request_id/comments" do
it "should return comment" do
post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment"
- response.status.should == 201
- json_response['note'].should == 'My comment'
+ expect(response.status).to eq(201)
+ expect(json_response['note']).to eq('My comment')
end
it "should return 400 if note is missing" do
post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 404 if note is attached to non existent merge request" do
post api("/projects/#{project.id}/merge_request/404/comments", user),
note: 'My comment'
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
describe "GET :id/merge_request/:merge_request_id/comments" do
it "should return merge_request comments" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.length.should == 1
- json_response.first['note'].should == "a comment on a MR"
- json_response.first['author']['id'].should == user.id
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['note']).to eq("a comment on a MR")
+ expect(json_response.first['author']['id']).to eq(user.id)
end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999/comments", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index f0619a1c80100aa9f390ec9a3a93d7712aee903c..effb0723476e843bc0e66101baf7e78480bf5b2e 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -8,92 +8,109 @@ describe API::API, api: true do
before { project.team << [user, :developer] }
- describe "GET /projects/:id/milestones" do
- it "should return project milestones" do
+ describe 'GET /projects/:id/milestones' do
+ it 'should return project milestones' do
get api("/projects/#{project.id}/milestones", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['title'].should == milestone.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(milestone.title)
end
- it "should return a 401 error if user not authenticated" do
+ it 'should return a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
- describe "GET /projects/:id/milestones/:milestone_id" do
- it "should return a project milestone by id" do
+ describe 'GET /projects/:id/milestones/:milestone_id' do
+ it 'should return a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
- response.status.should == 200
- json_response['title'].should == milestone.title
- json_response['iid'].should == milestone.iid
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq(milestone.title)
+ expect(json_response['iid']).to eq(milestone.iid)
end
- it "should return 401 error if user not authenticated" do
+ it 'should return 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
- it "should return a 404 error if milestone id not found" do
+ it 'should return a 404 error if milestone id not found' do
get api("/projects/#{project.id}/milestones/1234", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- describe "POST /projects/:id/milestones" do
- it "should create a new project milestone" do
+ describe 'POST /projects/:id/milestones' do
+ it 'should create a new project milestone' do
post api("/projects/#{project.id}/milestones", user), title: 'new milestone'
- response.status.should == 201
- json_response['title'].should == 'new milestone'
- json_response['description'].should be_nil
+ expect(response.status).to eq(201)
+ expect(json_response['title']).to eq('new milestone')
+ expect(json_response['description']).to be_nil
end
- it "should create a new project milestone with description and due date" do
+ it 'should create a new project milestone with description and due date' do
post api("/projects/#{project.id}/milestones", user),
title: 'new milestone', description: 'release', due_date: '2013-03-02'
- response.status.should == 201
- json_response['description'].should == 'release'
- json_response['due_date'].should == '2013-03-02'
+ expect(response.status).to eq(201)
+ expect(json_response['description']).to eq('release')
+ expect(json_response['due_date']).to eq('2013-03-02')
end
- it "should return a 400 error if title is missing" do
+ it 'should return a 400 error if title is missing' do
post api("/projects/#{project.id}/milestones", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
- describe "PUT /projects/:id/milestones/:milestone_id" do
- it "should update a project milestone" do
+ describe 'PUT /projects/:id/milestones/:milestone_id' do
+ it 'should update a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
title: 'updated title'
- response.status.should == 200
- json_response['title'].should == 'updated title'
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq('updated title')
end
- it "should return a 404 error if milestone id not found" do
+ it 'should return a 404 error if milestone id not found' do
put api("/projects/#{project.id}/milestones/1234", user),
title: 'updated title'
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do
- it "should update a project milestone" do
+ describe 'PUT /projects/:id/milestones/:milestone_id to close milestone' do
+ it 'should update a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response['state'].should == 'closed'
+ expect(json_response['state']).to eq('closed')
end
end
- describe "PUT /projects/:id/milestones/:milestone_id to test observer on close" do
- it "should create an activity event when an milestone is closed" do
- Event.should_receive(:create)
+ describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
+ it 'should create an activity event when an milestone is closed' do
+ expect(Event).to receive(:create)
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
end
end
+
+ describe 'GET /projects/:id/milestones/:milestone_id/issues' do
+ before do
+ milestone.issues << create(:issue)
+ end
+ it 'should return project issues for a particular milestone' do
+ get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['milestone']['title']).to eq(milestone.title)
+ end
+
+ it 'should return a 401 error if user not authenticated' do
+ get api("/projects/#{project.id}/milestones/#{milestone.id}/issues")
+ expect(response.status).to eq(401)
+ end
+ end
end
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index b8943ea0762fabcf8dee6eb9e22547d6edd20c9b..6ddaaa0a6dde9603476184e91a9b5814ccd0b34f 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -10,17 +10,17 @@ describe API::API, api: true do
context "when unauthenticated" do
it "should return authentication error" do
get api("/namespaces")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when authenticated as admin" do
it "admin: should return an array of all namespaces" do
get api("/namespaces", admin)
- response.status.should == 200
- json_response.should be_an Array
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
- json_response.length.should == Namespace.count
+ expect(json_response.length).to eq(Namespace.count)
end
end
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 7aa53787aedb666c0d1311b8f140e451d2452c42..8b177af4689b09b63ff434c891d7d4feebdfa358 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -16,42 +16,42 @@ describe API::API, api: true do
context "when noteable is an Issue" do
it "should return an array of issue notes" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['body'].should == issue_note.note
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['body']).to eq(issue_note.note)
end
it "should return a 404 error when issue id not found" do
get api("/projects/#{project.id}/issues/123/notes", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context "when noteable is a Snippet" do
it "should return an array of snippet notes" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['body'].should == snippet_note.note
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['body']).to eq(snippet_note.note)
end
it "should return a 404 error when snippet id not found" do
get api("/projects/#{project.id}/snippets/42/notes", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context "when noteable is a Merge Request" do
it "should return an array of merge_requests notes" do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['body'].should == merge_request_note.note
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['body']).to eq(merge_request_note.note)
end
it "should return a 404 error if merge request id not found" do
get api("/projects/#{project.id}/merge_requests/4444/notes", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
end
@@ -60,26 +60,26 @@ describe API::API, api: true do
context "when noteable is an Issue" do
it "should return an issue note by id" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user)
- response.status.should == 200
- json_response['body'].should == issue_note.note
+ expect(response.status).to eq(200)
+ expect(json_response['body']).to eq(issue_note.note)
end
it "should return a 404 error if issue note not found" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context "when noteable is a Snippet" do
it "should return a snippet note by id" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user)
- response.status.should == 200
- json_response['body'].should == snippet_note.note
+ expect(response.status).to eq(200)
+ expect(json_response['body']).to eq(snippet_note.note)
end
it "should return a 404 error if snippet note not found" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
end
@@ -88,47 +88,101 @@ describe API::API, api: true do
context "when noteable is an Issue" do
it "should create a new issue note" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
- response.status.should == 201
- json_response['body'].should == 'hi!'
- json_response['author']['username'].should == user.username
+ expect(response.status).to eq(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user.username)
end
it "should return a 400 bad request error if body not given" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 401 unauthorized error if user not authenticated" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when noteable is a Snippet" do
it "should create a new snippet note" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!'
- response.status.should == 201
- json_response['body'].should == 'hi!'
- json_response['author']['username'].should == user.username
+ expect(response.status).to eq(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user.username)
end
it "should return a 400 bad request error if body not given" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 401 unauthorized error if user not authenticated" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!'
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
it "should create an activity event when an issue note is created" do
- Event.should_receive(:create)
+ expect(Event).to receive(:create)
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
end
end
+
+ describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do
+ context 'when noteable is an Issue' do
+ it 'should return modified note' do
+ put api("/projects/#{project.id}/issues/#{issue.id}/"\
+ "notes/#{issue_note.id}", user), body: 'Hello!'
+ expect(response.status).to eq(200)
+ expect(json_response['body']).to eq('Hello!')
+ end
+
+ it 'should return a 404 error when note id not found' do
+ put api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user),
+ body: 'Hello!'
+ expect(response.status).to eq(404)
+ end
+
+ it 'should return a 400 bad request error if body not given' do
+ put api("/projects/#{project.id}/issues/#{issue.id}/"\
+ "notes/#{issue_note.id}", user)
+ expect(response.status).to eq(400)
+ end
+ end
+
+ context 'when noteable is a Snippet' do
+ it 'should return modified note' do
+ put api("/projects/#{project.id}/snippets/#{snippet.id}/"\
+ "notes/#{snippet_note.id}", user), body: 'Hello!'
+ expect(response.status).to eq(200)
+ expect(json_response['body']).to eq('Hello!')
+ end
+
+ it 'should return a 404 error when note id not found' do
+ put api("/projects/#{project.id}/snippets/#{snippet.id}/"\
+ "notes/123", user), body: "Hello!"
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when noteable is a Merge Request' do
+ it 'should return modified note' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
+ "notes/#{merge_request_note.id}", user), body: 'Hello!'
+ expect(response.status).to eq(200)
+ expect(json_response['body']).to eq('Hello!')
+ end
+
+ it 'should return a 404 error when note id not found' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
+ "notes/123", user), body: "Hello!"
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
end
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index cdb5e3d061278e36dba652fd7c5028f74474db24..81fe68de662c94a01a4d8090da89a50268262178 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -16,18 +16,18 @@ describe API::API, 'ProjectHooks', api: true do
context "authorized user" do
it "should return project hooks" do
get api("/projects/#{project.id}/hooks", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response.should be_an Array
- json_response.count.should == 1
- json_response.first['url'].should == "http://example.com"
+ expect(json_response).to be_an Array
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['url']).to eq("http://example.com")
end
end
context "unauthorized user" do
it "should not access project hooks" do
get api("/projects/#{project.id}/hooks", user3)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
end
@@ -36,26 +36,26 @@ describe API::API, 'ProjectHooks', api: true do
context "authorized user" do
it "should return a project hook" do
get api("/projects/#{project.id}/hooks/#{hook.id}", user)
- response.status.should == 200
- json_response['url'].should == hook.url
+ expect(response.status).to eq(200)
+ expect(json_response['url']).to eq(hook.url)
end
it "should return a 404 error if hook id is not available" do
get api("/projects/#{project.id}/hooks/1234", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
context "unauthorized user" do
it "should not access an existing hook" do
get api("/projects/#{project.id}/hooks/#{hook.id}", user3)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
it "should return a 404 error if hook id is not available" do
get api("/projects/#{project.id}/hooks/1234", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -65,17 +65,17 @@ describe API::API, 'ProjectHooks', api: true do
post api("/projects/#{project.id}/hooks", user),
url: "http://example.com", issues_events: true
}.to change {project.hooks.count}.by(1)
- response.status.should == 201
+ expect(response.status).to eq(201)
end
it "should return a 400 error if url not given" do
post api("/projects/#{project.id}/hooks", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 422 error if url not valid" do
post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
- response.status.should == 422
+ expect(response.status).to eq(422)
end
end
@@ -83,23 +83,23 @@ describe API::API, 'ProjectHooks', api: true do
it "should update an existing project hook" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user),
url: 'http://example.org', push_events: false
- response.status.should == 200
- json_response['url'].should == 'http://example.org'
+ expect(response.status).to eq(200)
+ expect(json_response['url']).to eq('http://example.org')
end
it "should return 404 error if hook id not found" do
put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should return 400 error if url is not given" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 422 error if url is not valid" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
- response.status.should == 422
+ expect(response.status).to eq(422)
end
end
@@ -108,22 +108,22 @@ describe API::API, 'ProjectHooks', api: true do
expect {
delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
}.to change {project.hooks.count}.by(-1)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return success when deleting hook" do
delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return success when deleting non existent hook" do
delete api("/projects/#{project.id}/hooks/42", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return a 405 error if hook id not given" do
delete api("/projects/#{project.id}/hooks", user)
- response.status.should == 405
+ expect(response.status).to eq(405)
end
end
end
diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb
index 836f21f3e0bae6b1fc0ed2a2edf8a64c941f209c..8419a364ed115501b4d512cd73878c5766f0bc40 100644
--- a/spec/requests/api/project_members_spec.rb
+++ b/spec/requests/api/project_members_spec.rb
@@ -15,23 +15,23 @@ describe API::API, api: true do
it "should return project team members" do
get api("/projects/#{project.id}/members", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.count.should == 2
- json_response.map { |u| u['username'] }.should include user.username
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.count).to eq(2)
+ expect(json_response.map { |u| u['username'] }).to include user.username
end
it "finds team members with query string" do
get api("/projects/#{project.id}/members", user), query: user.username
- response.status.should == 200
- json_response.should be_an Array
- json_response.count.should == 1
- json_response.first['username'].should == user.username
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['username']).to eq(user.username)
end
it "should return a 404 error if id not found" do
get api("/projects/9999/members", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -40,14 +40,14 @@ describe API::API, api: true do
it "should return project team member" do
get api("/projects/#{project.id}/members/#{user.id}", user)
- response.status.should == 200
- json_response['username'].should == user.username
- json_response['access_level'].should == ProjectMember::MASTER
+ expect(response.status).to eq(200)
+ expect(json_response['username']).to eq(user.username)
+ expect(json_response['access_level']).to eq(ProjectMember::MASTER)
end
it "should return a 404 error if user id not found" do
get api("/projects/#{project.id}/members/1234", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -58,9 +58,9 @@ describe API::API, api: true do
access_level: ProjectMember::DEVELOPER
}.to change { ProjectMember.count }.by(1)
- response.status.should == 201
- json_response['username'].should == user2.username
- json_response['access_level'].should == ProjectMember::DEVELOPER
+ expect(response.status).to eq(201)
+ expect(json_response['username']).to eq(user2.username)
+ expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER)
end
it "should return a 201 status if user is already project member" do
@@ -69,26 +69,26 @@ describe API::API, api: true do
expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: ProjectMember::DEVELOPER
- }.not_to change { ProjectMember.count }.by(1)
+ }.not_to change { ProjectMember.count }
- response.status.should == 201
- json_response['username'].should == user2.username
- json_response['access_level'].should == ProjectMember::DEVELOPER
+ expect(response.status).to eq(201)
+ expect(json_response['username']).to eq(user2.username)
+ expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER)
end
it "should return a 400 error when user id is not given" do
post api("/projects/#{project.id}/members", user), access_level: ProjectMember::MASTER
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 400 error when access level is not given" do
post api("/projects/#{project.id}/members", user), user_id: user2.id
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 422 error when access level is not known" do
post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234
- response.status.should == 422
+ expect(response.status).to eq(422)
end
end
@@ -97,24 +97,24 @@ describe API::API, api: true do
it "should update project team member" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: ProjectMember::MASTER
- response.status.should == 200
- json_response['username'].should == user3.username
- json_response['access_level'].should == ProjectMember::MASTER
+ expect(response.status).to eq(200)
+ expect(json_response['username']).to eq(user3.username)
+ expect(json_response['access_level']).to eq(ProjectMember::MASTER)
end
it "should return a 404 error if user_id is not found" do
put api("/projects/#{project.id}/members/1234", user), access_level: ProjectMember::MASTER
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should return a 400 error when access level is not given" do
put api("/projects/#{project.id}/members/#{user3.id}", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return a 422 error when access level is not known" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123
- response.status.should == 422
+ expect(response.status).to eq(422)
end
end
@@ -132,22 +132,22 @@ describe API::API, api: true do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
- }.to_not change { ProjectMember.count }.by(1)
+ }.to_not change { ProjectMember.count }
end
it "should return 200 if team member already removed" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
delete api("/projects/#{project.id}/members/#{user3.id}", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return 200 OK when the user was not member" do
expect {
delete api("/projects/#{project.id}/members/1000000", user)
}.to change { ProjectMember.count }.by(0)
- response.status.should == 200
- json_response['message'].should == "Access revoked"
- json_response['id'].should == 1000000
+ expect(response.status).to eq(200)
+ expect(json_response['message']).to eq("Access revoked")
+ expect(json_response['id']).to eq(1000000)
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 2c4b68c10b688402ec76dc2e63ff43c1ca827c62..f28dfea3ccf62e1e84085772d77bfa13e1a8f4b9 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1,122 +1,149 @@
+# -*- coding: utf-8 -*-
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
+ include Gitlab::CurrentSettings
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
+ let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
+ let(:project3) { create(:project, path: 'project3', creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
let(:project_member2) { create(:project_member, user: user3, project: project, access_level: ProjectMember::DEVELOPER) }
+ let(:user4) { create(:user) }
+ let(:project3) do
+ create(:project,
+ name: 'second_project',
+ path: 'second_project',
+ creator_id: user.id,
+ namespace: user.namespace,
+ merge_requests_enabled: false,
+ issues_enabled: false, wiki_enabled: false,
+ snippets_enabled: false, visibility_level: 0)
+ end
+ let(:project_member3) do
+ create(:project_member,
+ user: user4,
+ project: project3,
+ access_level: ProjectMember::MASTER)
+ end
+ let(:project4) do
+ create(:project,
+ name: 'third_project',
+ path: 'third_project',
+ creator_id: user4.id,
+ namespace: user4.namespace)
+ end
- describe "GET /projects" do
+ describe 'GET /projects' do
before { project }
- context "when unauthenticated" do
- it "should return authentication error" do
- get api("/projects")
- response.status.should == 401
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get api('/projects')
+ expect(response.status).to eq(401)
end
end
- context "when authenticated" do
- it "should return an array of projects" do
- get api("/projects", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['name'].should == project.name
- json_response.first['owner']['username'].should == user.username
+ context 'when authenticated' do
+ it 'should return an array of projects' do
+ get api('/projects', user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).to eq(project.name)
+ expect(json_response.first['owner']['username']).to eq(user.username)
+ end
+
+ context 'and using search' do
+ it 'should return searched project' do
+ get api('/projects', user), { search: project.name }
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ end
+ end
+
+ context 'and using sorting' do
+ before do
+ project2
+ project3
+ end
+
+ it 'should return the correct order when sorted by id' do
+ get api('/projects', user), { order_by: 'id', sort: 'desc'}
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(project3.id)
+ end
end
end
end
- describe "GET /projects/all" do
+ describe 'GET /projects/all' do
before { project }
- context "when unauthenticated" do
- it "should return authentication error" do
- get api("/projects/all")
- response.status.should == 401
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get api('/projects/all')
+ expect(response.status).to eq(401)
end
end
- context "when authenticated as regular user" do
- it "should return authentication error" do
- get api("/projects/all", user)
- response.status.should == 403
+ context 'when authenticated as regular user' do
+ it 'should return authentication error' do
+ get api('/projects/all', user)
+ expect(response.status).to eq(403)
end
end
- context "when authenticated as admin" do
- it "should return an array of all projects" do
- get api("/projects/all", admin)
- response.status.should == 200
- json_response.should be_an Array
+ context 'when authenticated as admin' do
+ it 'should return an array of all projects' do
+ get api('/projects/all', admin)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
project_name = project.name
- json_response.detect {
+ expect(json_response.detect {
|project| project['name'] == project_name
- }['name'].should == project_name
+ }['name']).to eq(project_name)
- json_response.detect {
+ expect(json_response.detect {
|project| project['owner']['username'] == user.username
- }['owner']['username'].should == user.username
+ }['owner']['username']).to eq(user.username)
end
end
end
- describe "POST /projects" do
- context "maximum number of projects reached" do
- before do
- (1..user2.projects_limit).each do |project|
- post api("/projects", user2), name: "foo#{project}"
- end
- end
-
- it "should not create new project" do
+ describe 'POST /projects' do
+ context 'maximum number of projects reached' do
+ it 'should not create new project and respond with 403' do
+ allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
expect {
- post api("/projects", user2), name: 'foo'
+ post api('/projects', user2), name: 'foo'
}.to change {Project.count}.by(0)
+ expect(response.status).to eq(403)
end
end
- it "should create new project without path" do
- expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1)
+ it 'should create new project without path and return 201' do
+ expect { post api('/projects', user), name: 'foo' }.
+ to change { Project.count }.by(1)
+ expect(response.status).to eq(201)
end
- it "should not create new project without name" do
- expect { post api("/projects", user) }.to_not change {Project.count}
+ it 'should create last project before reaching project limit' do
+ allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
+ post api('/projects', user2), name: 'foo'
+ expect(response.status).to eq(201)
end
- it "should return a 400 error if name not given" do
- post api("/projects", user)
- response.status.should == 400
- end
-
- it "should create last project before reaching project limit" do
- (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" }
- post api("/projects", user2), name: "foo"
- response.status.should == 201
- end
-
- it "should respond with 201 on success" do
- post api("/projects", user), name: 'foo'
- response.status.should == 201
- end
-
- it "should respond with 400 if name is not given" do
- post api("/projects", user)
- response.status.should == 400
- end
-
- it "should return a 403 error if project limit reached" do
- (1..user.projects_limit).each do |p|
- post api("/projects", user), name: "foo#{p}"
- end
- post api("/projects", user), name: 'bar'
- response.status.should == 403
+ it 'should not create new project without name and return 400' do
+ expect { post api('/projects', user) }.to_not change { Project.count }
+ expect(response.status).to eq(400)
end
it "should assign attributes to project" do
@@ -128,91 +155,108 @@ describe API::API, api: true do
wiki_enabled: false
})
- post api("/projects", user), project
+ post api('/projects', user), project
project.each_pair do |k,v|
- json_response[k.to_s].should == v
+ expect(json_response[k.to_s]).to eq(v)
end
end
- it "should set a project as public" do
+ it 'should set a project as public' do
project = attributes_for(:project, :public)
- post api("/projects", user), project
- json_response['public'].should be_true
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ post api('/projects', user), project
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
- it "should set a project as public using :public" do
+ it 'should set a project as public using :public' do
project = attributes_for(:project, { public: true })
- post api("/projects", user), project
- json_response['public'].should be_true
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ post api('/projects', user), project
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
- it "should set a project as internal" do
+ it 'should set a project as internal' do
project = attributes_for(:project, :internal)
- post api("/projects", user), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
+ post api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
- it "should set a project as internal overriding :public" do
+ it 'should set a project as internal overriding :public' do
project = attributes_for(:project, :internal, { public: true })
- post api("/projects", user), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
+ post api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
- it "should set a project as private" do
+ it 'should set a project as private' do
project = attributes_for(:project, :private)
- post api("/projects", user), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
+ post api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
- it "should set a project as private using :public" do
+ it 'should set a project as private using :public' do
project = attributes_for(:project, { public: false })
- post api("/projects", user), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
+ post api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ context 'when a visibility level is restricted' do
+ before do
+ @project = attributes_for(:project, { public: true })
+ allow_any_instance_of(ApplicationSetting).to(
+ receive(:restricted_visibility_levels).and_return([20])
+ )
+ end
+
+ it 'should not allow a non-admin to use a restricted visibility level' do
+ post api('/projects', user), @project
+ expect(response.status).to eq(400)
+ expect(json_response['message']['visibility_level'].first).to(
+ match('restricted by your GitLab administrator')
+ )
+ end
+
+ it 'should allow an admin to override restricted visibility settings' do
+ post api('/projects', admin), @project
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to(
+ eq(Gitlab::VisibilityLevel::PUBLIC)
+ )
+ end
end
end
- describe "POST /projects/user/:id" do
+ describe 'POST /projects/user/:id' do
before { project }
before { admin }
- it "should create new project without path" do
+ it 'should create new project without path and return 201' do
expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
+ expect(response.status).to eq(201)
end
- it "should not create new project without name" do
- expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count}
- end
-
- it "should respond with 201 on success" do
- post api("/projects/user/#{user.id}", admin), name: 'foo'
- response.status.should == 201
- end
+ it 'should respond with 400 on failure and not project' do
+ expect { post api("/projects/user/#{user.id}", admin) }.
+ to_not change { Project.count }
- it 'should respond with 400 on failure' do
- post api("/projects/user/#{user.id}", admin)
- response.status.should == 400
- json_response['message']['creator'].should == ['can\'t be blank']
- json_response['message']['namespace'].should == ['can\'t be blank']
- json_response['message']['name'].should == [
+ expect(response.status).to eq(400)
+ expect(json_response['message']['name']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
Gitlab::Regex.project_regex_message
- ]
- json_response['message']['path'].should == [
+ ])
+ expect(json_response['message']['path']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
Gitlab::Regex.send(:default_regex_message)
- ]
+ ])
end
- it "should assign attributes to project" do
+ it 'should assign attributes to project' do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
issues_enabled: false,
@@ -224,227 +268,217 @@ describe API::API, api: true do
project.each_pair do |k,v|
next if k == :path
- json_response[k.to_s].should == v
+ expect(json_response[k.to_s]).to eq(v)
end
end
- it "should set a project as public" do
+ it 'should set a project as public' do
project = attributes_for(:project, :public)
post api("/projects/user/#{user.id}", admin), project
- json_response['public'].should be_true
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
- it "should set a project as public using :public" do
+ it 'should set a project as public using :public' do
project = attributes_for(:project, { public: true })
post api("/projects/user/#{user.id}", admin), project
- json_response['public'].should be_true
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
- it "should set a project as internal" do
+ it 'should set a project as internal' do
project = attributes_for(:project, :internal)
post api("/projects/user/#{user.id}", admin), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
- it "should set a project as internal overriding :public" do
+ it 'should set a project as internal overriding :public' do
project = attributes_for(:project, :internal, { public: true })
post api("/projects/user/#{user.id}", admin), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
- it "should set a project as private" do
+ it 'should set a project as private' do
project = attributes_for(:project, :private)
post api("/projects/user/#{user.id}", admin), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
- it "should set a project as private using :public" do
+ it 'should set a project as private using :public' do
project = attributes_for(:project, { public: false })
post api("/projects/user/#{user.id}", admin), project
- json_response['public'].should be_false
- json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
- describe "GET /projects/:id" do
+ describe 'GET /projects/:id' do
before { project }
before { project_member }
- it "should return a project by id" do
+ it 'should return a project by id' do
get api("/projects/#{project.id}", user)
- response.status.should == 200
- json_response['name'].should == project.name
- json_response['owner']['username'].should == user.username
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['owner']['username']).to eq(user.username)
end
- it "should return a project by path name" do
+ it 'should return a project by path name' do
get api("/projects/#{project.id}", user)
- response.status.should == 200
- json_response['name'].should == project.name
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(project.name)
end
- it "should return a 404 error if not found" do
- get api("/projects/42", user)
- response.status.should == 404
- json_response['message'].should == '404 Not Found'
+ it 'should return a 404 error if not found' do
+ get api('/projects/42', user)
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
end
- it "should return a 404 error if user is not a member" do
+ it 'should return a 404 error if user is not a member' do
other_user = create(:user)
get api("/projects/#{project.id}", other_user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
describe 'permissions' do
context 'personal project' do
- before do
+ it 'Sets project access and returns 200' do
project.team << [user, :master]
get api("/projects/#{project.id}", user)
- end
- it { response.status.should == 200 }
- it { json_response['permissions']["project_access"]["access_level"].should == Gitlab::Access::MASTER }
- it { json_response['permissions']["group_access"].should be_nil }
+ expect(response.status).to eq(200)
+ expect(json_response['permissions']['project_access']['access_level']).
+ to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['group_access']).to be_nil
+ end
end
context 'group project' do
- before do
+ it 'should set the owner and return 200' do
project2 = create(:project, group: create(:group))
project2.group.add_owner(user)
get api("/projects/#{project2.id}", user)
- end
- it { response.status.should == 200 }
- it { json_response['permissions']["project_access"].should be_nil }
- it { json_response['permissions']["group_access"]["access_level"].should == Gitlab::Access::OWNER }
+ expect(response.status).to eq(200)
+ expect(json_response['permissions']['project_access']).to be_nil
+ expect(json_response['permissions']['group_access']['access_level']).
+ to eq(Gitlab::Access::OWNER)
+ end
end
end
end
- describe "GET /projects/:id/events" do
- before { project_member }
+ describe 'GET /projects/:id/events' do
+ before { project_member2 }
- it "should return a project events" do
+ it 'should return a project events' do
get api("/projects/#{project.id}/events", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
json_event = json_response.first
- json_event['action_name'].should == 'joined'
- json_event['project_id'].to_i.should == project.id
- json_event['author_username'].should == user.username
+ expect(json_event['action_name']).to eq('joined')
+ expect(json_event['project_id'].to_i).to eq(project.id)
+ expect(json_event['author_username']).to eq(user3.username)
end
- it "should return a 404 error if not found" do
- get api("/projects/42/events", user)
- response.status.should == 404
- json_response['message'].should == '404 Not Found'
+ it 'should return a 404 error if not found' do
+ get api('/projects/42/events', user)
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
end
- it "should return a 404 error if user is not a member" do
+ it 'should return a 404 error if user is not a member' do
other_user = create(:user)
get api("/projects/#{project.id}/events", other_user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- describe "GET /projects/:id/snippets" do
+ describe 'GET /projects/:id/snippets' do
before { snippet }
- it "should return an array of project snippets" do
+ it 'should return an array of project snippets' do
get api("/projects/#{project.id}/snippets", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['title'].should == snippet.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(snippet.title)
end
end
- describe "GET /projects/:id/snippets/:snippet_id" do
- it "should return a project snippet" do
+ describe 'GET /projects/:id/snippets/:snippet_id' do
+ it 'should return a project snippet' do
get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
- response.status.should == 200
- json_response['title'].should == snippet.title
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq(snippet.title)
end
- it "should return a 404 error if snippet id not found" do
+ it 'should return a 404 error if snippet id not found' do
get api("/projects/#{project.id}/snippets/1234", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- describe "POST /projects/:id/snippets" do
- it "should create a new project snippet" do
+ describe 'POST /projects/:id/snippets' do
+ it 'should create a new project snippet' do
post api("/projects/#{project.id}/snippets", user),
- title: 'api test', file_name: 'sample.rb', code: 'test'
- response.status.should == 201
- json_response['title'].should == 'api test'
+ title: 'api test', file_name: 'sample.rb', code: 'test',
+ visibility_level: '0'
+ expect(response.status).to eq(201)
+ expect(json_response['title']).to eq('api test')
end
- it "should return a 400 error if title is not given" do
- post api("/projects/#{project.id}/snippets", user),
- file_name: 'sample.rb', code: 'test'
- response.status.should == 400
- end
-
- it "should return a 400 error if file_name not given" do
- post api("/projects/#{project.id}/snippets", user),
- title: 'api test', code: 'test'
- response.status.should == 400
- end
-
- it "should return a 400 error if code not given" do
- post api("/projects/#{project.id}/snippets", user),
- title: 'api test', file_name: 'sample.rb'
- response.status.should == 400
+ it 'should return a 400 error if invalid snippet is given' do
+ post api("/projects/#{project.id}/snippets", user)
+ expect(status).to eq(400)
end
end
- describe "PUT /projects/:id/snippets/:shippet_id" do
- it "should update an existing project snippet" do
+ describe 'PUT /projects/:id/snippets/:shippet_id' do
+ it 'should update an existing project snippet' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
code: 'updated code'
- response.status.should == 200
- json_response['title'].should == 'example'
- snippet.reload.content.should == 'updated code'
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq('example')
+ expect(snippet.reload.content).to eq('updated code')
end
- it "should update an existing project snippet with new title" do
+ it 'should update an existing project snippet with new title' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
title: 'other api test'
- response.status.should == 200
- json_response['title'].should == 'other api test'
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq('other api test')
end
end
- describe "DELETE /projects/:id/snippets/:snippet_id" do
+ describe 'DELETE /projects/:id/snippets/:snippet_id' do
before { snippet }
- it "should delete existing project snippet" do
+ it 'should delete existing project snippet' do
expect {
delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
}.to change { Snippet.count }.by(-1)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it 'should return 404 when deleting unknown snippet id' do
delete api("/projects/#{project.id}/snippets/1234", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- describe "GET /projects/:id/snippets/:snippet_id/raw" do
- it "should get a raw project snippet" do
+ describe 'GET /projects/:id/snippets/:snippet_id/raw' do
+ it 'should get a raw project snippet' do
get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
- it "should return a 404 error if raw project snippet not found" do
+ it 'should return a 404 error if raw project snippet not found' do
get api("/projects/#{project.id}/snippets/5555/raw", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -452,51 +486,51 @@ describe API::API, api: true do
let(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
let(:deploy_key) { deploy_keys_project.deploy_key }
- describe "GET /projects/:id/keys" do
+ describe 'GET /projects/:id/keys' do
before { deploy_key }
- it "should return array of ssh keys" do
+ it 'should return array of ssh keys' do
get api("/projects/#{project.id}/keys", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['title'].should == deploy_key.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(deploy_key.title)
end
end
- describe "GET /projects/:id/keys/:key_id" do
- it "should return a single key" do
+ describe 'GET /projects/:id/keys/:key_id' do
+ it 'should return a single key' do
get api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
- response.status.should == 200
- json_response['title'].should == deploy_key.title
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq(deploy_key.title)
end
- it "should return 404 Not Found with invalid ID" do
+ it 'should return 404 Not Found with invalid ID' do
get api("/projects/#{project.id}/keys/404", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- describe "POST /projects/:id/keys" do
- it "should not create an invalid ssh key" do
- post api("/projects/#{project.id}/keys", user), { title: "invalid key" }
- response.status.should == 400
- json_response['message']['key'].should == [
+ describe 'POST /projects/:id/keys' do
+ it 'should not create an invalid ssh key' do
+ post api("/projects/#{project.id}/keys", user), { title: 'invalid key' }
+ expect(response.status).to eq(400)
+ expect(json_response['message']['key']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
'is invalid'
- ]
+ ])
end
it 'should not create a key without title' do
post api("/projects/#{project.id}/keys", user), key: 'some key'
- response.status.should == 400
- json_response['message']['title'].should == [
+ expect(response.status).to eq(400)
+ expect(json_response['message']['title']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)'
- ]
+ ])
end
- it "should create new ssh key" do
+ it 'should create new ssh key' do
key_attrs = attributes_for :key
expect {
post api("/projects/#{project.id}/keys", user), key_attrs
@@ -504,18 +538,18 @@ describe API::API, api: true do
end
end
- describe "DELETE /projects/:id/keys/:key_id" do
+ describe 'DELETE /projects/:id/keys/:key_id' do
before { deploy_key }
- it "should delete existing key" do
+ it 'should delete existing key' do
expect {
delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
}.to change{ project.deploy_keys.count }.by(-1)
end
- it "should return 404 Not Found with invalid ID" do
+ it 'should return 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/keys/404", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
end
@@ -524,70 +558,70 @@ describe API::API, api: true do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) }
- describe "POST /projects/:id/fork/:forked_from_id" do
+ describe 'POST /projects/:id/fork/:forked_from_id' do
let(:new_project_fork_source) { create(:project, :public) }
it "shouldn't available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
- it "should allow project to be forked from an existing project" do
- project_fork_target.forked?.should_not be_true
+ it 'should allow project to be forked from an existing project' do
+ expect(project_fork_target.forked?).not_to be_truthy
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
- response.status.should == 201
+ expect(response.status).to eq(201)
project_fork_target.reload
- project_fork_target.forked_from_project.id.should == project_fork_source.id
- project_fork_target.forked_project_link.should_not be_nil
- project_fork_target.forked?.should be_true
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.forked_project_link).not_to be_nil
+ expect(project_fork_target.forked?).to be_truthy
end
- it "should fail if forked_from project which does not exist" do
+ it 'should fail if forked_from project which does not exist' do
post api("/projects/#{project_fork_target.id}/fork/9999", admin)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
- it "should fail with 409 if already forked" do
+ it 'should fail with 409 if already forked' do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
project_fork_target.reload
- project_fork_target.forked_from_project.id.should == project_fork_source.id
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
- response.status.should == 409
+ expect(response.status).to eq(409)
project_fork_target.reload
- project_fork_target.forked_from_project.id.should == project_fork_source.id
- project_fork_target.forked?.should be_true
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.forked?).to be_truthy
end
end
- describe "DELETE /projects/:id/fork" do
+ describe 'DELETE /projects/:id/fork' do
it "shouldn't available for non admin users" do
delete api("/projects/#{project_fork_target.id}/fork", user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
- it "should make forked project unforked" do
+ it 'should make forked project unforked' do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
project_fork_target.reload
- project_fork_target.forked_from_project.should_not be_nil
- project_fork_target.forked?.should be_true
+ expect(project_fork_target.forked_from_project).not_to be_nil
+ expect(project_fork_target.forked?).to be_truthy
delete api("/projects/#{project_fork_target.id}/fork", admin)
- response.status.should == 200
+ expect(response.status).to eq(200)
project_fork_target.reload
- project_fork_target.forked_from_project.should be_nil
- project_fork_target.forked?.should_not be_true
+ expect(project_fork_target.forked_from_project).to be_nil
+ expect(project_fork_target.forked?).not_to be_truthy
end
- it "should be idempotent if not forked" do
- project_fork_target.forked_from_project.should be_nil
+ it 'should be idempotent if not forked' do
+ expect(project_fork_target.forked_from_project).to be_nil
delete api("/projects/#{project_fork_target.id}/fork", admin)
- response.status.should == 200
- project_fork_target.reload.forked_from_project.should be_nil
+ expect(response.status).to eq(200)
+ expect(project_fork_target.reload.forked_from_project).to be_nil
end
end
end
- describe "GET /projects/search/:query" do
+ describe 'GET /projects/search/:query' do
let!(:query) { 'query'}
let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
let!(:pre) { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
@@ -599,73 +633,185 @@ describe API::API, api: true do
let!(:public) { create(:empty_project, :public, name: "public #{query}") }
let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
- context "when unauthenticated" do
- it "should return authentication error" do
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
get api("/projects/search/#{query}")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
- context "when authenticated" do
- it "should return an array of projects" do
+ context 'when authenticated' do
+ it 'should return an array of projects' do
get api("/projects/search/#{query}",user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.size.should == 6
- json_response.each {|project| project['name'].should =~ /.*query.*/}
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(6)
+ json_response.each {|project| expect(project['name']).to match(/.*query.*/)}
end
end
- context "when authenticated as a different user" do
- it "should return matching public projects" do
+ context 'when authenticated as a different user' do
+ it 'should return matching public projects' do
get api("/projects/search/#{query}", user2)
- response.status.should == 200
- json_response.should be_an Array
- json_response.size.should == 2
- json_response.each {|project| project['name'].should =~ /(internal|public) query/}
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(2)
+ json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)}
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id̈́' do
+ before { project }
+ before { user }
+ before { user3 }
+ before { user4 }
+ before { project3 }
+ before { project4 }
+ before { project_member3 }
+ before { project_member2 }
+
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ project_param = { name: 'bar' }
+ put api("/projects/#{project.id}"), project_param
+ expect(response.status).to eq(401)
+ end
+ end
+
+ context 'when authenticated as project owner' do
+ it 'should update name' do
+ project_param = { name: 'bar' }
+ put api("/projects/#{project.id}", user), project_param
+ expect(response.status).to eq(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'should update visibility_level' do
+ project_param = { visibility_level: 20 }
+ put api("/projects/#{project3.id}", user), project_param
+ expect(response.status).to eq(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'should not update name to existing name' do
+ project_param = { name: project3.name }
+ put api("/projects/#{project.id}", user), project_param
+ expect(response.status).to eq(400)
+ expect(json_response['message']['name']).to eq(['has already been taken'])
+ end
+
+ it 'should update path & name to existing path & name in different namespace' do
+ project_param = { path: project4.path, name: project4.name }
+ put api("/projects/#{project3.id}", user), project_param
+ expect(response.status).to eq(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+ end
+
+ context 'when authenticated as project master' do
+ it 'should update path' do
+ project_param = { path: 'bar' }
+ put api("/projects/#{project3.id}", user4), project_param
+ expect(response.status).to eq(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'should update other attributes' do
+ project_param = { issues_enabled: true,
+ wiki_enabled: true,
+ snippets_enabled: true,
+ merge_requests_enabled: true,
+ description: 'new description' }
+
+ put api("/projects/#{project3.id}", user4), project_param
+ expect(response.status).to eq(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'should not update path to existing path' do
+ project_param = { path: project.path }
+ put api("/projects/#{project3.id}", user4), project_param
+ expect(response.status).to eq(400)
+ expect(json_response['message']['path']).to eq(['has already been taken'])
+ end
+
+ it 'should not update name' do
+ project_param = { name: 'bar' }
+ put api("/projects/#{project3.id}", user4), project_param
+ expect(response.status).to eq(403)
+ end
+
+ it 'should not update visibility_level' do
+ project_param = { visibility_level: 20 }
+ put api("/projects/#{project3.id}", user4), project_param
+ expect(response.status).to eq(403)
+ end
+ end
+
+ context 'when authenticated as project developer' do
+ it 'should not update other attributes' do
+ project_param = { path: 'bar',
+ issues_enabled: true,
+ wiki_enabled: true,
+ snippets_enabled: true,
+ merge_requests_enabled: true,
+ description: 'new description' }
+ put api("/projects/#{project.id}", user3), project_param
+ expect(response.status).to eq(403)
end
end
end
- describe "DELETE /projects/:id" do
- context "when authenticated as user" do
- it "should remove project" do
+ describe 'DELETE /projects/:id' do
+ context 'when authenticated as user' do
+ it 'should remove project' do
expect(GitlabShellWorker).to(
receive(:perform_async).with(:remove_repository,
/#{project.path_with_namespace}/)
).twice
delete api("/projects/#{project.id}", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
- it "should not remove a project if not an owner" do
+ it 'should not remove a project if not an owner' do
user3 = create(:user)
project.team << [user3, :developer]
delete api("/projects/#{project.id}", user3)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
- it "should not remove a non existing project" do
- delete api("/projects/1328", user)
- response.status.should == 404
+ it 'should not remove a non existing project' do
+ delete api('/projects/1328', user)
+ expect(response.status).to eq(404)
end
- it "should not remove a project not attached to user" do
+ it 'should not remove a project not attached to user' do
delete api("/projects/#{project.id}", user2)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
- context "when authenticated as admin" do
- it "should remove any existing project" do
+ context 'when authenticated as admin' do
+ it 'should remove any existing project' do
delete api("/projects/#{project.id}", admin)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
- it "should not remove a non existing project" do
- delete api("/projects/1328", admin)
- response.status.should == 404
+ it 'should not remove a non existing project' do
+ delete api('/projects/1328', admin)
+ expect(response.status).to eq(404)
end
end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index beae71c02d9343b5c73939f32205a62fc97c2ec5..729970153d1f196da09b54543095603f2b2dce71 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -16,9 +16,9 @@ describe API::API, api: true do
describe "GET /projects/:id/repository/tags" do
it "should return an array of project tags" do
get api("/projects/#{project.id}/repository/tags", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['name'].should == project.repo.tags.sort_by(&:name).reverse.first.name
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name)
end
end
@@ -29,8 +29,8 @@ describe API::API, api: true do
tag_name: 'v7.0.1',
ref: 'master'
- response.status.should == 201
- json_response['name'].should == 'v7.0.1'
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq('v7.0.1')
end
end
@@ -46,9 +46,9 @@ describe API::API, api: true do
ref: 'master',
message: 'Release 7.1.0'
- response.status.should == 201
- json_response['name'].should == 'v7.1.0'
- json_response['message'].should == 'Release 7.1.0'
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq('v7.1.0')
+ expect(json_response['message']).to eq('Release 7.1.0')
end
end
@@ -56,35 +56,35 @@ describe API::API, api: true do
post api("/projects/#{project.id}/repository/tags", user2),
tag_name: 'v1.9.0',
ref: '621491c677087aa243f165eab467bfdfbee00be1'
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it 'should return 400 if tag name is invalid' do
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v 1.0.0',
ref: 'master'
- response.status.should == 400
- json_response['message'].should == 'Tag name invalid'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Tag name invalid')
end
it 'should return 400 if tag already exists' do
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v8.0.0',
ref: 'master'
- response.status.should == 201
+ expect(response.status).to eq(201)
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v8.0.0',
ref: 'master'
- response.status.should == 400
- json_response['message'].should == 'Tag already exists'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Tag already exists')
end
it 'should return 400 if ref name is invalid' do
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'mytag',
ref: 'foo'
- response.status.should == 400
- json_response['message'].should == 'Invalid reference name'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Invalid reference name')
end
end
@@ -94,19 +94,27 @@ describe API::API, api: true do
it "should return project commits" do
get api("/projects/#{project.id}/repository/tree", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
- json_response.should be_an Array
- json_response.first['name'].should == 'encoding'
- json_response.first['type'].should == 'tree'
- json_response.first['mode'].should == '040000'
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).to eq('encoding')
+ expect(json_response.first['type']).to eq('tree')
+ expect(json_response.first['mode']).to eq('040000')
+ end
+
+ it 'should return a 404 for unknown ref' do
+ get api("/projects/#{project.id}/repository/tree?ref_name=foo", user)
+ expect(response.status).to eq(404)
+
+ expect(json_response).to be_an Object
+ json_response['message'] == '404 Tree Not Found'
end
end
context "unauthorized user" do
it "should not return project commits" do
get api("/projects/#{project.id}/repository/tree")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
@@ -114,36 +122,44 @@ describe API::API, api: true do
describe "GET /projects/:id/repository/blobs/:sha" do
it "should get the raw file contents" do
get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return 404 for invalid branch_name" do
get api("/projects/#{project.id}/repository/blobs/invalid_branch_name?filepath=README.md", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should return 404 for invalid file" do
get api("/projects/#{project.id}/repository/blobs/master?filepath=README.invalid", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
it "should return a 400 error if filepath is missing" do
get api("/projects/#{project.id}/repository/blobs/master", user)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
describe "GET /projects/:id/repository/commits/:sha/blob" do
it "should get the raw file contents" do
get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
end
describe "GET /projects/:id/repository/raw_blobs/:sha" do
it "should get the raw file contents" do
get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
+ end
+
+ it 'should return a 404 for unknown blob' do
+ get api("/projects/#{project.id}/repository/raw_blobs/123456", user)
+ expect(response.status).to eq(404)
+
+ expect(json_response).to be_an Object
+ json_response['message'] == '404 Blob Not Found'
end
end
@@ -151,83 +167,83 @@ describe API::API, api: true do
it "should get the archive" do
get api("/projects/#{project.id}/repository/archive", user)
repo_name = project.repository.name.gsub("\.git", "")
- response.status.should == 200
- response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/
- response.content_type.should == MIME::Types.type_for('file.tar.gz').first.content_type
+ expect(response.status).to eq(200)
+ expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/)
+ expect(response.content_type).to eq(MIME::Types.type_for('file.tar.gz').first.content_type)
end
it "should get the archive.zip" do
get api("/projects/#{project.id}/repository/archive.zip", user)
repo_name = project.repository.name.gsub("\.git", "")
- response.status.should == 200
- response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.zip\"/
- response.content_type.should == MIME::Types.type_for('file.zip').first.content_type
+ expect(response.status).to eq(200)
+ expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.zip\"/)
+ expect(response.content_type).to eq(MIME::Types.type_for('file.zip').first.content_type)
end
it "should get the archive.tar.bz2" do
get api("/projects/#{project.id}/repository/archive.tar.bz2", user)
repo_name = project.repository.name.gsub("\.git", "")
- response.status.should == 200
- response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/
- response.content_type.should == MIME::Types.type_for('file.tar.bz2').first.content_type
+ expect(response.status).to eq(200)
+ expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/)
+ expect(response.content_type).to eq(MIME::Types.type_for('file.tar.bz2').first.content_type)
end
it "should return 404 for invalid sha" do
get api("/projects/#{project.id}/repository/archive/?sha=xxx", user)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
describe 'GET /projects/:id/repository/compare' do
it "should compare branches" do
get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'feature'
- response.status.should == 200
- json_response['commits'].should be_present
- json_response['diffs'].should be_present
+ expect(response.status).to eq(200)
+ expect(json_response['commits']).to be_present
+ expect(json_response['diffs']).to be_present
end
it "should compare tags" do
get api("/projects/#{project.id}/repository/compare", user), from: 'v1.0.0', to: 'v1.1.0'
- response.status.should == 200
- json_response['commits'].should be_present
- json_response['diffs'].should be_present
+ expect(response.status).to eq(200)
+ expect(json_response['commits']).to be_present
+ expect(json_response['diffs']).to be_present
end
it "should compare commits" do
get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.id, to: sample_commit.parent_id
- response.status.should == 200
- json_response['commits'].should be_empty
- json_response['diffs'].should be_empty
- json_response['compare_same_ref'].should be_false
+ expect(response.status).to eq(200)
+ expect(json_response['commits']).to be_empty
+ expect(json_response['diffs']).to be_empty
+ expect(json_response['compare_same_ref']).to be_falsey
end
it "should compare commits in reverse order" do
get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.parent_id, to: sample_commit.id
- response.status.should == 200
- json_response['commits'].should be_present
- json_response['diffs'].should be_present
+ expect(response.status).to eq(200)
+ expect(json_response['commits']).to be_present
+ expect(json_response['diffs']).to be_present
end
it "should compare same refs" do
get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'master'
- response.status.should == 200
- json_response['commits'].should be_empty
- json_response['diffs'].should be_empty
- json_response['compare_same_ref'].should be_true
+ expect(response.status).to eq(200)
+ expect(json_response['commits']).to be_empty
+ expect(json_response['diffs']).to be_empty
+ expect(json_response['compare_same_ref']).to be_truthy
end
end
describe 'GET /projects/:id/repository/contributors' do
it 'should return valid data' do
get api("/projects/#{project.id}/repository/contributors", user)
- response.status.should == 200
- json_response.should be_an Array
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
contributor = json_response.first
- contributor['email'].should == 'dmitriy.zaporozhets@gmail.com'
- contributor['name'].should == 'Dmitriy Zaporozhets'
- contributor['commits'].should == 13
- contributor['additions'].should == 0
- contributor['deletions'].should == 0
+ expect(contributor['email']).to eq('dmitriy.zaporozhets@gmail.com')
+ expect(contributor['name']).to eq('Dmitriy Zaporozhets')
+ expect(contributor['commits']).to eq(13)
+ expect(contributor['additions']).to eq(0)
+ expect(contributor['deletions']).to eq(0)
end
end
end
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index d8282d0696bd92fda584150692deaf2bfd938747..51c543578df747fec3bb0a5e5d3a7103248137a5 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -9,13 +9,13 @@ describe API::API, api: true do
it "should update gitlab-ci settings" do
put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secret-token', project_url: "http://ci.example.com/projects/1"
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return if required fields missing" do
put api("/projects/#{project.id}/services/gitlab-ci", user), project_url: "http://ci.example.com/projects/1", active: true
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
@@ -23,8 +23,8 @@ describe API::API, api: true do
it "should update gitlab-ci settings" do
delete api("/projects/#{project.id}/services/gitlab-ci", user)
- response.status.should == 200
- project.gitlab_ci_service.should be_nil
+ expect(response.status).to eq(200)
+ expect(project.gitlab_ci_service).to be_nil
end
end
@@ -33,15 +33,15 @@ describe API::API, api: true do
put api("/projects/#{project.id}/services/hipchat", user),
token: 'secret-token', room: 'test'
- response.status.should == 200
- project.hipchat_service.should_not be_nil
+ expect(response.status).to eq(200)
+ expect(project.hipchat_service).not_to be_nil
end
it 'should return if required fields missing' do
put api("/projects/#{project.id}/services/gitlab-ci", user),
token: 'secret-token', active: true
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
@@ -49,8 +49,8 @@ describe API::API, api: true do
it 'should delete hipchat settings' do
delete api("/projects/#{project.id}/services/hipchat", user)
- response.status.should == 200
- project.hipchat_service.should be_nil
+ expect(response.status).to eq(200)
+ expect(project.hipchat_service).to be_nil
end
end
end
diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb
index 57b2e6cbd6abcd12b815c260481acb4ef1dd15b1..fbd57b34a58d32308e969cdddf4b7b681011b645 100644
--- a/spec/requests/api/session_spec.rb
+++ b/spec/requests/api/session_spec.rb
@@ -9,13 +9,13 @@ describe API::API, api: true do
context "when valid password" do
it "should return private token" do
post api("/session"), email: user.email, password: '12345678'
- response.status.should == 201
+ expect(response.status).to eq(201)
- json_response['email'].should == user.email
- json_response['private_token'].should == user.private_token
- json_response['is_admin'].should == user.is_admin?
- json_response['can_create_project'].should == user.can_create_project?
- json_response['can_create_group'].should == user.can_create_group?
+ expect(json_response['email']).to eq(user.email)
+ expect(json_response['private_token']).to eq(user.private_token)
+ expect(json_response['is_admin']).to eq(user.is_admin?)
+ expect(json_response['can_create_project']).to eq(user.can_create_project?)
+ expect(json_response['can_create_group']).to eq(user.can_create_group?)
end
end
@@ -48,30 +48,30 @@ describe API::API, api: true do
context "when invalid password" do
it "should return authentication error" do
post api("/session"), email: user.email, password: '123'
- response.status.should == 401
+ expect(response.status).to eq(401)
- json_response['email'].should be_nil
- json_response['private_token'].should be_nil
+ expect(json_response['email']).to be_nil
+ expect(json_response['private_token']).to be_nil
end
end
context "when empty password" do
it "should return authentication error" do
post api("/session"), email: user.email
- response.status.should == 401
+ expect(response.status).to eq(401)
- json_response['email'].should be_nil
- json_response['private_token'].should be_nil
+ expect(json_response['email']).to be_nil
+ expect(json_response['private_token']).to be_nil
end
end
context "when empty name" do
it "should return authentication error" do
post api("/session"), password: user.password
- response.status.should == 401
+ expect(response.status).to eq(401)
- json_response['email'].should be_nil
- json_response['private_token'].should be_nil
+ expect(json_response['email']).to be_nil
+ expect(json_response['private_token']).to be_nil
end
end
end
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 5784ae8c23a65f55cacaeef0bb30066e8b4fe4eb..a9d86bbce6c9165d715cecd235be557d27f5519a 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -13,23 +13,23 @@ describe API::API, api: true do
context "when no user" do
it "should return authentication error" do
get api("/hooks")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when not an admin" do
it "should return forbidden error" do
get api("/hooks", user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
end
context "when authenticated as admin" do
it "should return an array of hooks" do
get api("/hooks", admin)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['url'].should == hook.url
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['url']).to eq(hook.url)
end
end
end
@@ -43,7 +43,7 @@ describe API::API, api: true do
it "should respond with 400 if url not given" do
post api("/hooks", admin)
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should not create new hook without url" do
@@ -56,13 +56,13 @@ describe API::API, api: true do
describe "GET /hooks/:id" do
it "should return hook by id" do
get api("/hooks/#{hook.id}", admin)
- response.status.should == 200
- json_response['event_name'].should == 'project_create'
+ expect(response.status).to eq(200)
+ expect(json_response['event_name']).to eq('project_create')
end
it "should return 404 on failure" do
get api("/hooks/404", admin)
- response.status.should == 404
+ expect(response.status).to eq(404)
end
end
@@ -75,7 +75,7 @@ describe API::API, api: true do
it "should return success if hook id not found" do
delete api("/hooks/12345", admin)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 113a39b870ed9e5e85d293a51458ef1426650a5f..081400cdedda826ca3c1d1b69c6a861493a3a992 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,30 +11,30 @@ describe API::API, api: true do
context "when unauthenticated" do
it "should return authentication error" do
get api("/users")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context "when authenticated" do
it "should return an array of users" do
get api("/users", user)
- response.status.should == 200
- json_response.should be_an Array
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
username = user.username
- json_response.detect {
+ expect(json_response.detect {
|user| user['username'] == username
- }['username'].should == username
+ }['username']).to eq(username)
end
end
context "when admin" do
it "should return an array of users" do
get api("/users", admin)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first.keys.should include 'email'
- json_response.first.keys.should include 'extern_uid'
- json_response.first.keys.should include 'can_create_project'
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to include 'email'
+ expect(json_response.first.keys).to include 'identities'
+ expect(json_response.first.keys).to include 'can_create_project'
end
end
end
@@ -42,19 +42,19 @@ describe API::API, api: true do
describe "GET /users/:id" do
it "should return a user by id" do
get api("/users/#{user.id}", user)
- response.status.should == 200
- json_response['username'].should == user.username
+ expect(response.status).to eq(200)
+ expect(json_response['username']).to eq(user.username)
end
it "should return a 401 if unauthenticated" do
get api("/users/9998")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
it "should return a 404 error if user id not found" do
get api("/users/9999", user)
- response.status.should == 404
- json_response['message'].should == '404 Not found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Not found')
end
end
@@ -69,36 +69,36 @@ describe API::API, api: true do
it "should create user with correct attributes" do
post api('/users', admin), attributes_for(:user, admin: true, can_create_group: true)
- response.status.should == 201
+ expect(response.status).to eq(201)
user_id = json_response['id']
new_user = User.find(user_id)
- new_user.should_not == nil
- new_user.admin.should == true
- new_user.can_create_group.should == true
+ expect(new_user).not_to eq(nil)
+ expect(new_user.admin).to eq(true)
+ expect(new_user.can_create_group).to eq(true)
end
it "should create non-admin user" do
post api('/users', admin), attributes_for(:user, admin: false, can_create_group: false)
- response.status.should == 201
+ expect(response.status).to eq(201)
user_id = json_response['id']
new_user = User.find(user_id)
- new_user.should_not == nil
- new_user.admin.should == false
- new_user.can_create_group.should == false
+ expect(new_user).not_to eq(nil)
+ expect(new_user.admin).to eq(false)
+ expect(new_user.can_create_group).to eq(false)
end
it "should create non-admin users by default" do
post api('/users', admin), attributes_for(:user)
- response.status.should == 201
+ expect(response.status).to eq(201)
user_id = json_response['id']
new_user = User.find(user_id)
- new_user.should_not == nil
- new_user.admin.should == false
+ expect(new_user).not_to eq(nil)
+ expect(new_user.admin).to eq(false)
end
it "should return 201 Created on success" do
post api("/users", admin), attributes_for(:user, projects_limit: 3)
- response.status.should == 201
+ expect(response.status).to eq(201)
end
it "should not create user with invalid email" do
@@ -106,22 +106,22 @@ describe API::API, api: true do
email: 'invalid email',
password: 'password',
name: 'test'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 400 error if name not given' do
post api('/users', admin), email: 'test@example.com', password: 'pass1234'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 400 error if password not given' do
post api('/users', admin), email: 'test@example.com', name: 'test'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it "should return 400 error if email not given" do
post api('/users', admin), password: 'pass1234', name: 'test'
- response.status.should == 400
+ expect(response.status).to eq(400)
end
it 'should return 400 error if user does not validate' do
@@ -132,20 +132,20 @@ describe API::API, api: true do
name: 'test',
bio: 'g' * 256,
projects_limit: -1
- response.status.should == 400
- json_response['message']['password'].
- should == ['is too short (minimum is 8 characters)']
- json_response['message']['bio'].
- should == ['is too long (maximum is 255 characters)']
- json_response['message']['projects_limit'].
- should == ['must be greater than or equal to 0']
- json_response['message']['username'].
- should == [Gitlab::Regex.send(:default_regex_message)]
+ expect(response.status).to eq(400)
+ expect(json_response['message']['password']).
+ to eq(['is too short (minimum is 8 characters)'])
+ expect(json_response['message']['bio']).
+ to eq(['is too long (maximum is 255 characters)'])
+ expect(json_response['message']['projects_limit']).
+ to eq(['must be greater than or equal to 0'])
+ expect(json_response['message']['username']).
+ to eq([Gitlab::Regex.send(:default_regex_message)])
end
it "shouldn't available for non admin users" do
post api("/users", user), attributes_for(:user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
context 'with existing user' do
@@ -165,8 +165,8 @@ describe API::API, api: true do
password: 'password',
username: 'foo'
}.to change { User.count }.by(0)
- response.status.should == 409
- json_response['message'].should == 'Email has already been taken'
+ expect(response.status).to eq(409)
+ expect(json_response['message']).to eq('Email has already been taken')
end
it 'should return 409 conflict error if same username exists' do
@@ -177,34 +177,18 @@ describe API::API, api: true do
password: 'password',
username: 'test'
end.to change { User.count }.by(0)
- response.status.should == 409
- json_response['message'].should == 'Username has already been taken'
+ expect(response.status).to eq(409)
+ expect(json_response['message']).to eq('Username has already been taken')
end
end
end
describe "GET /users/sign_up" do
- context 'enabled' do
- before do
- Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
- end
-
- it "should return sign up page if signup is enabled" do
- get "/users/sign_up"
- response.status.should == 200
- end
- end
- context 'disabled' do
- before do
- Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
- end
-
- it "should redirect to sign in page if signup is disabled" do
- get "/users/sign_up"
- response.status.should == 302
- response.should redirect_to(new_user_session_path)
- end
+ it "should redirect to sign in page" do
+ get "/users/sign_up"
+ expect(response.status).to eq(302)
+ expect(response).to redirect_to(new_user_session_path)
end
end
@@ -215,55 +199,55 @@ describe API::API, api: true do
it "should update user with new bio" do
put api("/users/#{user.id}", admin), {bio: 'new test bio'}
- response.status.should == 200
- json_response['bio'].should == 'new test bio'
- user.reload.bio.should == 'new test bio'
+ expect(response.status).to eq(200)
+ expect(json_response['bio']).to eq('new test bio')
+ expect(user.reload.bio).to eq('new test bio')
end
it 'should update user with his own email' do
put api("/users/#{user.id}", admin), email: user.email
- response.status.should == 200
- json_response['email'].should == user.email
- user.reload.email.should == user.email
+ expect(response.status).to eq(200)
+ expect(json_response['email']).to eq(user.email)
+ expect(user.reload.email).to eq(user.email)
end
it 'should update user with his own username' do
put api("/users/#{user.id}", admin), username: user.username
- response.status.should == 200
- json_response['username'].should == user.username
- user.reload.username.should == user.username
+ expect(response.status).to eq(200)
+ expect(json_response['username']).to eq(user.username)
+ expect(user.reload.username).to eq(user.username)
end
it "should update admin status" do
put api("/users/#{user.id}", admin), {admin: true}
- response.status.should == 200
- json_response['is_admin'].should == true
- user.reload.admin.should == true
+ expect(response.status).to eq(200)
+ expect(json_response['is_admin']).to eq(true)
+ expect(user.reload.admin).to eq(true)
end
it "should not update admin status" do
put api("/users/#{admin_user.id}", admin), {can_create_group: false}
- response.status.should == 200
- json_response['is_admin'].should == true
- admin_user.reload.admin.should == true
- admin_user.can_create_group.should == false
+ expect(response.status).to eq(200)
+ expect(json_response['is_admin']).to eq(true)
+ expect(admin_user.reload.admin).to eq(true)
+ expect(admin_user.can_create_group).to eq(false)
end
it "should not allow invalid update" do
put api("/users/#{user.id}", admin), {email: 'invalid email'}
- response.status.should == 400
- user.reload.email.should_not == 'invalid email'
+ expect(response.status).to eq(400)
+ expect(user.reload.email).not_to eq('invalid email')
end
it "shouldn't available for non admin users" do
put api("/users/#{user.id}", user), attributes_for(:user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it "should return 404 for non-existing user" do
put api("/users/999999", admin), {bio: 'update should fail'}
- response.status.should == 404
- json_response['message'].should == '404 Not found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Not found')
end
it 'should return 400 error if user does not validate' do
@@ -274,15 +258,15 @@ describe API::API, api: true do
name: 'test',
bio: 'g' * 256,
projects_limit: -1
- response.status.should == 400
- json_response['message']['password'].
- should == ['is too short (minimum is 8 characters)']
- json_response['message']['bio'].
- should == ['is too long (maximum is 255 characters)']
- json_response['message']['projects_limit'].
- should == ['must be greater than or equal to 0']
- json_response['message']['username'].
- should == [Gitlab::Regex.send(:default_regex_message)]
+ expect(response.status).to eq(400)
+ expect(json_response['message']['password']).
+ to eq(['is too short (minimum is 8 characters)'])
+ expect(json_response['message']['bio']).
+ to eq(['is too long (maximum is 255 characters)'])
+ expect(json_response['message']['projects_limit']).
+ to eq(['must be greater than or equal to 0'])
+ expect(json_response['message']['username']).
+ to eq([Gitlab::Regex.send(:default_regex_message)])
end
context "with existing user" do
@@ -294,15 +278,15 @@ describe API::API, api: true do
it 'should return 409 conflict error if email address exists' do
put api("/users/#{@user.id}", admin), email: 'test@example.com'
- response.status.should == 409
- @user.reload.email.should == @user.email
+ expect(response.status).to eq(409)
+ expect(@user.reload.email).to eq(@user.email)
end
it 'should return 409 conflict error if username taken' do
@user_id = User.all.last.id
put api("/users/#{@user.id}", admin), username: 'test'
- response.status.should == 409
- @user.reload.username.should == @user.username
+ expect(response.status).to eq(409)
+ expect(@user.reload.username).to eq(@user.username)
end
end
end
@@ -312,14 +296,14 @@ describe API::API, api: true do
it "should not create invalid ssh key" do
post api("/users/#{user.id}/keys", admin), { title: "invalid key" }
- response.status.should == 400
- json_response['message'].should == '400 (Bad request) "key" not given'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('400 (Bad request) "key" not given')
end
it 'should not create key without title' do
post api("/users/#{user.id}/keys", admin), key: 'some key'
- response.status.should == 400
- json_response['message'].should == '400 (Bad request) "title" not given'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('400 (Bad request) "title" not given')
end
it "should create ssh key" do
@@ -336,24 +320,24 @@ describe API::API, api: true do
context 'when unauthenticated' do
it 'should return authentication error' do
get api("/users/#{user.id}/keys")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
context 'when authenticated' do
it 'should return 404 for non-existing user' do
get api('/users/999999/keys', admin)
- response.status.should == 404
- json_response['message'].should == '404 User Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 User Not Found')
end
it 'should return array of ssh keys' do
user.keys << key
user.save
get api("/users/#{user.id}/keys", admin)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first['title'].should == key.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(key.title)
end
end
end
@@ -364,7 +348,7 @@ describe API::API, api: true do
context 'when unauthenticated' do
it 'should return authentication error' do
delete api("/users/#{user.id}/keys/42")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
@@ -375,21 +359,21 @@ describe API::API, api: true do
expect {
delete api("/users/#{user.id}/keys/#{key.id}", admin)
}.to change { user.keys.count }.by(-1)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it 'should return 404 error if user not found' do
user.keys << key
user.save
delete api("/users/999999/keys/#{key.id}", admin)
- response.status.should == 404
- json_response['message'].should == '404 User Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 User Not Found')
end
it 'should return 404 error if key not foud' do
delete api("/users/#{user.id}/keys/42", admin)
- response.status.should == 404
- json_response['message'].should == '404 Key Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Key Not Found')
end
end
end
@@ -399,42 +383,42 @@ describe API::API, api: true do
it "should delete user" do
delete api("/users/#{user.id}", admin)
- response.status.should == 200
+ expect(response.status).to eq(200)
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
- json_response['email'].should == user.email
+ expect(json_response['email']).to eq(user.email)
end
it "should not delete for unauthenticated user" do
delete api("/users/#{user.id}")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
it "shouldn't available for non admin users" do
delete api("/users/#{user.id}", user)
- response.status.should == 403
+ expect(response.status).to eq(403)
end
it "should return 404 for non-existing user" do
delete api("/users/999999", admin)
- response.status.should == 404
- json_response['message'].should == '404 User Not Found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 User Not Found')
end
end
describe "GET /user" do
it "should return current user" do
get api("/user", user)
- response.status.should == 200
- json_response['email'].should == user.email
- json_response['is_admin'].should == user.is_admin?
- json_response['can_create_project'].should == user.can_create_project?
- json_response['can_create_group'].should == user.can_create_group?
- json_response['projects_limit'].should == user.projects_limit
+ expect(response.status).to eq(200)
+ expect(json_response['email']).to eq(user.email)
+ expect(json_response['is_admin']).to eq(user.is_admin?)
+ expect(json_response['can_create_project']).to eq(user.can_create_project?)
+ expect(json_response['can_create_group']).to eq(user.can_create_group?)
+ expect(json_response['projects_limit']).to eq(user.projects_limit)
end
it "should return 401 error if user is unauthenticated" do
get api("/user")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
@@ -442,7 +426,7 @@ describe API::API, api: true do
context "when unauthenticated" do
it "should return authentication error" do
get api("/user/keys")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
@@ -451,9 +435,9 @@ describe API::API, api: true do
user.keys << key
user.save
get api("/user/keys", user)
- response.status.should == 200
- json_response.should be_an Array
- json_response.first["title"].should == key.title
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first["title"]).to eq(key.title)
end
end
end
@@ -463,14 +447,14 @@ describe API::API, api: true do
user.keys << key
user.save
get api("/user/keys/#{key.id}", user)
- response.status.should == 200
- json_response["title"].should == key.title
+ expect(response.status).to eq(200)
+ expect(json_response["title"]).to eq(key.title)
end
it "should return 404 Not Found within invalid ID" do
get api("/user/keys/42", user)
- response.status.should == 404
- json_response['message'].should == '404 Not found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Not found')
end
it "should return 404 error if admin accesses user's ssh key" do
@@ -478,8 +462,8 @@ describe API::API, api: true do
user.save
admin
get api("/user/keys/#{key.id}", admin)
- response.status.should == 404
- json_response['message'].should == '404 Not found'
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Not found')
end
end
@@ -489,29 +473,29 @@ describe API::API, api: true do
expect {
post api("/user/keys", user), key_attrs
}.to change{ user.keys.count }.by(1)
- response.status.should == 201
+ expect(response.status).to eq(201)
end
it "should return a 401 error if unauthorized" do
post api("/user/keys"), title: 'some title', key: 'some key'
- response.status.should == 401
+ expect(response.status).to eq(401)
end
it "should not create ssh key without key" do
post api("/user/keys", user), title: 'title'
- response.status.should == 400
- json_response['message'].should == '400 (Bad request) "key" not given'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('400 (Bad request) "key" not given')
end
it 'should not create ssh key without title' do
post api('/user/keys', user), key: 'some key'
- response.status.should == 400
- json_response['message'].should == '400 (Bad request) "title" not given'
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('400 (Bad request) "title" not given')
end
it "should not create ssh key without title" do
post api("/user/keys", user), key: "somekey"
- response.status.should == 400
+ expect(response.status).to eq(400)
end
end
@@ -522,19 +506,19 @@ describe API::API, api: true do
expect {
delete api("/user/keys/#{key.id}", user)
}.to change{user.keys.count}.by(-1)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return success if key ID not found" do
delete api("/user/keys/42", user)
- response.status.should == 200
+ expect(response.status).to eq(200)
end
it "should return 401 error if unauthorized" do
user.keys << key
user.save
delete api("/user/keys/#{key.id}")
- response.status.should == 401
+ expect(response.status).to eq(401)
end
end
end
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 7fe18ff47c3d1d93b59128821e360aa6c5f206a1..bf8abcfb00f86ba4293748f0a5e232c82357ff6e 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -12,47 +12,47 @@ require 'spec_helper'
# DELETE /admin/users/:id(.:format) admin/users#destroy
describe Admin::UsersController, "routing" do
it "to #team_update" do
- put("/admin/users/1/team_update").should route_to('admin/users#team_update', id: '1')
+ expect(put("/admin/users/1/team_update")).to route_to('admin/users#team_update', id: '1')
end
it "to #block" do
- put("/admin/users/1/block").should route_to('admin/users#block', id: '1')
+ expect(put("/admin/users/1/block")).to route_to('admin/users#block', id: '1')
end
it "to #unblock" do
- put("/admin/users/1/unblock").should route_to('admin/users#unblock', id: '1')
+ expect(put("/admin/users/1/unblock")).to route_to('admin/users#unblock', id: '1')
end
it "to #index" do
- get("/admin/users").should route_to('admin/users#index')
+ expect(get("/admin/users")).to route_to('admin/users#index')
end
it "to #show" do
- get("/admin/users/1").should route_to('admin/users#show', id: '1')
+ expect(get("/admin/users/1")).to route_to('admin/users#show', id: '1')
end
it "to #create" do
- post("/admin/users").should route_to('admin/users#create')
+ expect(post("/admin/users")).to route_to('admin/users#create')
end
it "to #new" do
- get("/admin/users/new").should route_to('admin/users#new')
+ expect(get("/admin/users/new")).to route_to('admin/users#new')
end
it "to #edit" do
- get("/admin/users/1/edit").should route_to('admin/users#edit', id: '1')
+ expect(get("/admin/users/1/edit")).to route_to('admin/users#edit', id: '1')
end
it "to #show" do
- get("/admin/users/1").should route_to('admin/users#show', id: '1')
+ expect(get("/admin/users/1")).to route_to('admin/users#show', id: '1')
end
it "to #update" do
- put("/admin/users/1").should route_to('admin/users#update', id: '1')
+ expect(put("/admin/users/1")).to route_to('admin/users#update', id: '1')
end
it "to #destroy" do
- delete("/admin/users/1").should route_to('admin/users#destroy', id: '1')
+ expect(delete("/admin/users/1")).to route_to('admin/users#destroy', id: '1')
end
end
@@ -67,11 +67,11 @@ end
# DELETE /admin/projects/:id(.:format) admin/projects#destroy {id: /[^\/]+/}
describe Admin::ProjectsController, "routing" do
it "to #index" do
- get("/admin/projects").should route_to('admin/projects#index')
+ expect(get("/admin/projects")).to route_to('admin/projects#index')
end
it "to #show" do
- get("/admin/projects/gitlab").should route_to('admin/projects#show', id: 'gitlab')
+ expect(get("/admin/projects/gitlab")).to route_to('admin/projects#show', namespace_id: 'gitlab')
end
end
@@ -81,19 +81,19 @@ end
# admin_hook DELETE /admin/hooks/:id(.:format) admin/hooks#destroy
describe Admin::HooksController, "routing" do
it "to #test" do
- get("/admin/hooks/1/test").should route_to('admin/hooks#test', hook_id: '1')
+ expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', hook_id: '1')
end
it "to #index" do
- get("/admin/hooks").should route_to('admin/hooks#index')
+ expect(get("/admin/hooks")).to route_to('admin/hooks#index')
end
it "to #create" do
- post("/admin/hooks").should route_to('admin/hooks#create')
+ expect(post("/admin/hooks")).to route_to('admin/hooks#create')
end
it "to #destroy" do
- delete("/admin/hooks/1").should route_to('admin/hooks#destroy', id: '1')
+ expect(delete("/admin/hooks/1")).to route_to('admin/hooks#destroy', id: '1')
end
end
@@ -101,21 +101,21 @@ end
# admin_logs GET /admin/logs(.:format) admin/logs#show
describe Admin::LogsController, "routing" do
it "to #show" do
- get("/admin/logs").should route_to('admin/logs#show')
+ expect(get("/admin/logs")).to route_to('admin/logs#show')
end
end
# admin_background_jobs GET /admin/background_jobs(.:format) admin/background_jobs#show
describe Admin::BackgroundJobsController, "routing" do
it "to #show" do
- get("/admin/background_jobs").should route_to('admin/background_jobs#show')
+ expect(get("/admin/background_jobs")).to route_to('admin/background_jobs#show')
end
end
# admin_root /admin(.:format) admin/dashboard#index
describe Admin::DashboardController, "routing" do
it "to #index" do
- get("/admin").should route_to('admin/dashboard#index')
+ expect(get("/admin")).to route_to('admin/dashboard#index')
end
end
diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb
index 112b825e0230cd8a1c04d269b6890e01bd9358d8..24592942a967e77b20d38dc6391550af2890239d 100644
--- a/spec/routing/notifications_routing_spec.rb
+++ b/spec/routing/notifications_routing_spec.rb
@@ -3,11 +3,11 @@ require "spec_helper"
describe Profiles::NotificationsController do
describe "routing" do
it "routes to #show" do
- get("/profile/notifications").should route_to("profiles/notifications#show")
+ expect(get("/profile/notifications")).to route_to("profiles/notifications#show")
end
it "routes to #update" do
- put("/profile/notifications").should route_to("profiles/notifications#update")
+ expect(put("/profile/notifications")).to route_to("profiles/notifications#update")
end
end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 4b2eb42c709b1b870f4f05115af1655913e032cb..d9bd91f59901352348f0bb50f7109667d8994af8 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -12,86 +12,88 @@ require 'spec_helper'
# Examples
#
# # Default behavior
-# it_behaves_like "RESTful project resources" do
+# it_behaves_like 'RESTful project resources' do
# let(:controller) { 'issues' }
# end
#
# # Customizing actions
-# it_behaves_like "RESTful project resources" do
+# it_behaves_like 'RESTful project resources' do
# let(:actions) { [:index] }
# let(:controller) { 'issues' }
# end
-shared_examples "RESTful project resources" do
+shared_examples 'RESTful project resources' do
let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
- it "to #index" do
- get("/gitlab/gitlabhq/#{controller}").should route_to("projects/#{controller}#index", project_id: 'gitlab/gitlabhq') if actions.include?(:index)
+ it 'to #index' do
+ expect(get("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index)
end
- it "to #create" do
- post("/gitlab/gitlabhq/#{controller}").should route_to("projects/#{controller}#create", project_id: 'gitlab/gitlabhq') if actions.include?(:create)
+ it 'to #create' do
+ expect(post("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create)
end
- it "to #new" do
- get("/gitlab/gitlabhq/#{controller}/new").should route_to("projects/#{controller}#new", project_id: 'gitlab/gitlabhq') if actions.include?(:new)
+ it 'to #new' do
+ expect(get("/gitlab/gitlabhq/#{controller}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new)
end
- it "to #edit" do
- get("/gitlab/gitlabhq/#{controller}/1/edit").should route_to("projects/#{controller}#edit", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:edit)
+ it 'to #edit' do
+ expect(get("/gitlab/gitlabhq/#{controller}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
end
- it "to #show" do
- get("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#show", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:show)
+ it 'to #show' do
+ expect(get("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show)
end
- it "to #update" do
- put("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#update", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:update)
+ it 'to #update' do
+ expect(put("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update)
end
- it "to #destroy" do
- delete("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#destroy", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:destroy)
+ it 'to #destroy' do
+ expect(delete("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
end
end
-# projects POST /projects(.:format) projects#create
-# new_project GET /projects/new(.:format) projects#new
-# fork_project POST /:id/fork(.:format) projects#fork
-# files_project GET /:id/files(.:format) projects#files
-# edit_project GET /:id/edit(.:format) projects#edit
-# project GET /:id(.:format) projects#show
-# PUT /:id(.:format) projects#update
-# DELETE /:id(.:format) projects#destroy
-describe ProjectsController, "routing" do
- it "to #create" do
- post("/projects").should route_to('projects#create')
+# projects POST /projects(.:format) projects#create
+# new_project GET /projects/new(.:format) projects#new
+# files_project GET /:id/files(.:format) projects#files
+# edit_project GET /:id/edit(.:format) projects#edit
+# project GET /:id(.:format) projects#show
+# PUT /:id(.:format) projects#update
+# DELETE /:id(.:format) projects#destroy
+# markdown_preview_project POST /:id/markdown_preview(.:format) projects#markdown_preview
+describe ProjectsController, 'routing' do
+ it 'to #create' do
+ expect(post('/projects')).to route_to('projects#create')
end
- it "to #new" do
- get("/projects/new").should route_to('projects#new')
+ it 'to #new' do
+ expect(get('/projects/new')).to route_to('projects#new')
end
- it "to #fork" do
- post("/gitlab/gitlabhq/fork").should route_to('projects#fork', id: 'gitlab/gitlabhq')
+ it 'to #edit' do
+ expect(get('/gitlab/gitlabhq/edit')).to route_to('projects#edit', namespace_id: 'gitlab', id: 'gitlabhq')
end
- it "to #edit" do
- get("/gitlab/gitlabhq/edit").should route_to('projects#edit', id: 'gitlab/gitlabhq')
+ it 'to #autocomplete_sources' do
+ expect(get('/gitlab/gitlabhq/autocomplete_sources')).to route_to('projects#autocomplete_sources', namespace_id: 'gitlab', id: 'gitlabhq')
end
- it "to #autocomplete_sources" do
- get('/gitlab/gitlabhq/autocomplete_sources').should route_to('projects#autocomplete_sources', id: "gitlab/gitlabhq")
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq')
end
- it "to #show" do
- get("/gitlab/gitlabhq").should route_to('projects#show', id: 'gitlab/gitlabhq')
+ it 'to #update' do
+ expect(put('/gitlab/gitlabhq')).to route_to('projects#update', namespace_id: 'gitlab', id: 'gitlabhq')
end
- it "to #update" do
- put("/gitlab/gitlabhq").should route_to('projects#update', id: 'gitlab/gitlabhq')
+ it 'to #destroy' do
+ expect(delete('/gitlab/gitlabhq')).to route_to('projects#destroy', namespace_id: 'gitlab', id: 'gitlabhq')
end
- it "to #destroy" do
- delete("/gitlab/gitlabhq").should route_to('projects#destroy', id: 'gitlab/gitlabhq')
+ it 'to #markdown_preview' do
+ expect(post('/gitlab/gitlabhq/markdown_preview')).to(
+ route_to('projects#markdown_preview', namespace_id: 'gitlab', id: 'gitlabhq')
+ )
end
end
@@ -101,16 +103,16 @@ end
# edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) projects/wikis#edit
# project_wiki GET /:project_id/wikis/:id(.:format) projects/wikis#show
# DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy
-describe Projects::WikisController, "routing" do
- it "to #pages" do
- get("/gitlab/gitlabhq/wikis/pages").should route_to('projects/wikis#pages', project_id: 'gitlab/gitlabhq')
+describe Projects::WikisController, 'routing' do
+ it 'to #pages' do
+ expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #history" do
- get("/gitlab/gitlabhq/wikis/1/history").should route_to('projects/wikis#history', project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #history' do
+ expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:create, :edit, :show, :destroy] }
let(:controller) { 'wikis' }
end
@@ -120,45 +122,45 @@ end
# tags_project_repository GET /:project_id/repository/tags(.:format) projects/repositories#tags
# archive_project_repository GET /:project_id/repository/archive(.:format) projects/repositories#archive
# edit_project_repository GET /:project_id/repository/edit(.:format) projects/repositories#edit
-describe Projects::RepositoriesController, "routing" do
- it "to #archive" do
- get("/gitlab/gitlabhq/repository/archive").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq')
+describe Projects::RepositoriesController, 'routing' do
+ it 'to #archive' do
+ expect(get('/gitlab/gitlabhq/repository/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #archive format:zip" do
- get("/gitlab/gitlabhq/repository/archive.zip").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'zip')
+ it 'to #archive format:zip' do
+ expect(get('/gitlab/gitlabhq/repository/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip')
end
- it "to #archive format:tar.bz2" do
- get("/gitlab/gitlabhq/repository/archive.tar.bz2").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'tar.bz2')
+ it 'to #archive format:tar.bz2' do
+ expect(get('/gitlab/gitlabhq/repository/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2')
end
- it "to #show" do
- get("/gitlab/gitlabhq/repository").should route_to('projects/repositories#show', project_id: 'gitlab/gitlabhq')
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/repository')).to route_to('projects/repositories#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
-describe Projects::BranchesController, "routing" do
- it "to #branches" do
- get("/gitlab/gitlabhq/branches").should route_to('projects/branches#index', project_id: 'gitlab/gitlabhq')
- delete("/gitlab/gitlabhq/branches/feature%2345").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
- delete("/gitlab/gitlabhq/branches/feature%2B45").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
- delete("/gitlab/gitlabhq/branches/feature@45").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
- delete("/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
- delete("/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
- delete("/gitlab/gitlabhq/branches/feature@45/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
+describe Projects::BranchesController, 'routing' do
+ it 'to #branches' do
+ expect(get('/gitlab/gitlabhq/branches')).to route_to('projects/branches#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2345')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2B45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(delete('/gitlab/gitlabhq/branches/feature@45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
end
end
-describe Projects::TagsController, "routing" do
- it "to #tags" do
- get("/gitlab/gitlabhq/tags").should route_to('projects/tags#index', project_id: 'gitlab/gitlabhq')
- delete("/gitlab/gitlabhq/tags/feature%2345").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
- delete("/gitlab/gitlabhq/tags/feature%2B45").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
- delete("/gitlab/gitlabhq/tags/feature@45").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
- delete("/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
- delete("/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
- delete("/gitlab/gitlabhq/tags/feature@45/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
+describe Projects::TagsController, 'routing' do
+ it 'to #tags' do
+ expect(get('/gitlab/gitlabhq/tags')).to route_to('projects/tags#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2345')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2B45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(delete('/gitlab/gitlabhq/tags/feature@45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
end
end
@@ -170,8 +172,8 @@ end
# project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show
# PUT /:project_id/deploy_keys/:id(.:format) deploy_keys#update
# DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
-describe Projects::DeployKeysController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::DeployKeysController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'deploy_keys' }
end
end
@@ -179,8 +181,8 @@ end
# project_protected_branches GET /:project_id/protected_branches(.:format) protected_branches#index
# POST /:project_id/protected_branches(.:format) protected_branches#create
# project_protected_branch DELETE /:project_id/protected_branches/:id(.:format) protected_branches#destroy
-describe Projects::ProtectedBranchesController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::ProtectedBranchesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'protected_branches' }
end
@@ -189,21 +191,21 @@ end
# switch_project_refs GET /:project_id/refs/switch(.:format) refs#switch
# logs_tree_project_ref GET /:project_id/refs/:id/logs_tree(.:format) refs#logs_tree
# logs_file_project_ref GET /:project_id/refs/:id/logs_tree/:path(.:format) refs#logs_tree
-describe Projects::RefsController, "routing" do
- it "to #switch" do
- get("/gitlab/gitlabhq/refs/switch").should route_to('projects/refs#switch', project_id: 'gitlab/gitlabhq')
- end
-
- it "to #logs_tree" do
- get("/gitlab/gitlabhq/refs/stable/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable')
- get("/gitlab/gitlabhq/refs/feature%2345/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45')
- get("/gitlab/gitlabhq/refs/feature%2B45/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45')
- get("/gitlab/gitlabhq/refs/feature@45/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45')
- get("/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss')
+describe Projects::RefsController, 'routing' do
+ it 'to #switch' do
+ expect(get('/gitlab/gitlabhq/refs/switch')).to route_to('projects/refs#switch', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #logs_tree' do
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
+ expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'files.scss')
end
end
@@ -219,36 +221,36 @@ end
# project_merge_request GET /:project_id/merge_requests/:id(.:format) projects/merge_requests#show
# PUT /:project_id/merge_requests/:id(.:format) projects/merge_requests#update
# DELETE /:project_id/merge_requests/:id(.:format) projects/merge_requests#destroy
-describe Projects::MergeRequestsController, "routing" do
- it "to #diffs" do
- get("/gitlab/gitlabhq/merge_requests/1/diffs").should route_to('projects/merge_requests#diffs', project_id: 'gitlab/gitlabhq', id: '1')
+describe Projects::MergeRequestsController, 'routing' do
+ it 'to #diffs' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it "to #automerge" do
- post('/gitlab/gitlabhq/merge_requests/1/automerge').should route_to(
+ it 'to #automerge' do
+ expect(post('/gitlab/gitlabhq/merge_requests/1/automerge')).to route_to(
'projects/merge_requests#automerge',
- project_id: 'gitlab/gitlabhq', id: '1'
+ namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1'
)
end
- it "to #automerge_check" do
- get("/gitlab/gitlabhq/merge_requests/1/automerge_check").should route_to('projects/merge_requests#automerge_check', project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #automerge_check' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1/automerge_check')).to route_to('projects/merge_requests#automerge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it "to #branch_from" do
- get("/gitlab/gitlabhq/merge_requests/branch_from").should route_to('projects/merge_requests#branch_from', project_id: 'gitlab/gitlabhq')
+ it 'to #branch_from' do
+ expect(get('/gitlab/gitlabhq/merge_requests/branch_from')).to route_to('projects/merge_requests#branch_from', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #branch_to" do
- get("/gitlab/gitlabhq/merge_requests/branch_to").should route_to('projects/merge_requests#branch_to', project_id: 'gitlab/gitlabhq')
+ it 'to #branch_to' do
+ expect(get('/gitlab/gitlabhq/merge_requests/branch_to')).to route_to('projects/merge_requests#branch_to', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #show" do
- get("/gitlab/gitlabhq/merge_requests/1.diff").should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'diff')
- get("/gitlab/gitlabhq/merge_requests/1.patch").should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'patch')
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
+ expect(get('/gitlab/gitlabhq/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'merge_requests' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
@@ -262,37 +264,37 @@ end
# project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
# PUT /:project_id/snippets/:id(.:format) snippets#update
# DELETE /:project_id/snippets/:id(.:format) snippets#destroy
-describe SnippetsController, "routing" do
- it "to #raw" do
- get("/gitlab/gitlabhq/snippets/1/raw").should route_to('projects/snippets#raw', project_id: 'gitlab/gitlabhq', id: '1')
+describe SnippetsController, 'routing' do
+ it 'to #raw' do
+ expect(get('/gitlab/gitlabhq/snippets/1/raw')).to route_to('projects/snippets#raw', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it "to #index" do
- get("/gitlab/gitlabhq/snippets").should route_to("projects/snippets#index", project_id: 'gitlab/gitlabhq')
+ it 'to #index' do
+ expect(get('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #create" do
- post("/gitlab/gitlabhq/snippets").should route_to("projects/snippets#create", project_id: 'gitlab/gitlabhq')
+ it 'to #create' do
+ expect(post('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #new" do
- get("/gitlab/gitlabhq/snippets/new").should route_to("projects/snippets#new", project_id: 'gitlab/gitlabhq')
+ it 'to #new' do
+ expect(get('/gitlab/gitlabhq/snippets/new')).to route_to('projects/snippets#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #edit" do
- get("/gitlab/gitlabhq/snippets/1/edit").should route_to("projects/snippets#edit", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #edit' do
+ expect(get('/gitlab/gitlabhq/snippets/1/edit')).to route_to('projects/snippets#edit', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it "to #show" do
- get("/gitlab/gitlabhq/snippets/1").should route_to("projects/snippets#show", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it "to #update" do
- put("/gitlab/gitlabhq/snippets/1").should route_to("projects/snippets#update", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #update' do
+ expect(put('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#update', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it "to #destroy" do
- delete("/gitlab/gitlabhq/snippets/1").should route_to("projects/snippets#destroy", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #destroy' do
+ expect(delete('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
end
@@ -300,24 +302,24 @@ end
# project_hooks GET /:project_id/hooks(.:format) hooks#index
# POST /:project_id/hooks(.:format) hooks#create
# project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy
-describe Projects::HooksController, "routing" do
- it "to #test" do
- get("/gitlab/gitlabhq/hooks/1/test").should route_to('projects/hooks#test', project_id: 'gitlab/gitlabhq', id: '1')
+describe Projects::HooksController, 'routing' do
+ it 'to #test' do
+ expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'hooks' }
end
end
# project_commit GET /:project_id/commit/:id(.:format) commit#show {id: /[[:alnum:]]{6,40}/, project_id: /[^\/]+/}
-describe Projects::CommitController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/commit/4246fb").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb')
- get("/gitlab/gitlabhq/commit/4246fb.diff").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'diff')
- get("/gitlab/gitlabhq/commit/4246fb.patch").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'patch')
- get("/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5')
+describe Projects::CommitController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/commit/4246fb')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fb')
+ expect(get('/gitlab/gitlabhq/commit/4246fb.diff')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fb', format: 'diff')
+ expect(get('/gitlab/gitlabhq/commit/4246fb.patch')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fb', format: 'patch')
+ expect(get('/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5')
end
end
@@ -325,28 +327,25 @@ end
# project_commits GET /:project_id/commits(.:format) commits#index
# POST /:project_id/commits(.:format) commits#create
# project_commit GET /:project_id/commits/:id(.:format) commits#show
-describe Projects::CommitsController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::CommitsController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:show] }
let(:controller) { 'commits' }
end
- it "to #show" do
- get("/gitlab/gitlabhq/commits/master.atom").should route_to('projects/commits#show', project_id: 'gitlab/gitlabhq', id: "master", format: "atom")
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/commits/master.atom')).to route_to('projects/commits#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'atom')
end
end
-# project_team_members GET /:project_id/team_members(.:format) team_members#index
-# POST /:project_id/team_members(.:format) team_members#create
-# new_project_team_member GET /:project_id/team_members/new(.:format) team_members#new
-# edit_project_team_member GET /:project_id/team_members/:id/edit(.:format) team_members#edit
-# project_team_member GET /:project_id/team_members/:id(.:format) team_members#show
-# PUT /:project_id/team_members/:id(.:format) team_members#update
-# DELETE /:project_id/team_members/:id(.:format) team_members#destroy
-describe Projects::TeamMembersController, "routing" do
- it_behaves_like "RESTful project resources" do
- let(:actions) { [:new, :create, :update, :destroy] }
- let(:controller) { 'team_members' }
+# project_project_members GET /:project_id/project_members(.:format) project_members#index
+# POST /:project_id/project_members(.:format) project_members#create
+# PUT /:project_id/project_members/:id(.:format) project_members#update
+# DELETE /:project_id/project_members/:id(.:format) project_members#destroy
+describe Projects::ProjectMembersController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:index, :create, :update, :destroy] }
+ let(:controller) { 'project_members' }
end
end
@@ -357,17 +356,17 @@ end
# project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
# PUT /:project_id/milestones/:id(.:format) milestones#update
# DELETE /:project_id/milestones/:id(.:format) milestones#destroy
-describe Projects::MilestonesController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::MilestonesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'milestones' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
end
# project_labels GET /:project_id/labels(.:format) labels#index
-describe Projects::LabelsController, "routing" do
- it "to #index" do
- get("/gitlab/gitlabhq/labels").should route_to('projects/labels#index', project_id: 'gitlab/gitlabhq')
+describe Projects::LabelsController, 'routing' do
+ it 'to #index' do
+ expect(get('/gitlab/gitlabhq/labels')).to route_to('projects/labels#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
@@ -381,84 +380,114 @@ end
# project_issue GET /:project_id/issues/:id(.:format) issues#show
# PUT /:project_id/issues/:id(.:format) issues#update
# DELETE /:project_id/issues/:id(.:format) issues#destroy
-describe Projects::IssuesController, "routing" do
- it "to #bulk_update" do
- post("/gitlab/gitlabhq/issues/bulk_update").should route_to('projects/issues#bulk_update', project_id: 'gitlab/gitlabhq')
+describe Projects::IssuesController, 'routing' do
+ it 'to #bulk_update' do
+ expect(post('/gitlab/gitlabhq/issues/bulk_update')).to route_to('projects/issues#bulk_update', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'issues' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
end
-# preview_project_notes POST /:project_id/notes/preview(.:format) notes#preview
# project_notes GET /:project_id/notes(.:format) notes#index
# POST /:project_id/notes(.:format) notes#create
# project_note DELETE /:project_id/notes/:id(.:format) notes#destroy
-describe Projects::NotesController, "routing" do
- it "to #preview" do
- post("/gitlab/gitlabhq/notes/preview").should route_to('projects/notes#preview', project_id: 'gitlab/gitlabhq')
- end
-
- it_behaves_like "RESTful project resources" do
+describe Projects::NotesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'notes' }
end
end
# project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::BlameController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/blame/master/app/models/project.rb").should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
- get("/gitlab/gitlabhq/blame/master/files.scss").should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
+describe Projects::BlameController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/blame/master/app/models/project.rb')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
+ expect(get('/gitlab/gitlabhq/blame/master/files.scss')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
end
end
# project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::BlobController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/blob/master/app/models/project.rb").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
- get("/gitlab/gitlabhq/blob/master/app/models/compare.rb").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/compare.rb')
- get("/gitlab/gitlabhq/blob/master/files.scss").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
+describe Projects::BlobController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/blob/master/app/models/project.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
+ expect(get('/gitlab/gitlabhq/blob/master/app/models/compare.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/compare.rb')
+ expect(get('/gitlab/gitlabhq/blob/master/app/models/diff.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/diff.js')
+ expect(get('/gitlab/gitlabhq/blob/master/files.scss')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
end
end
# project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::TreeController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/tree/master/app/models/project.rb").should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
- get("/gitlab/gitlabhq/tree/master/files.scss").should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
+describe Projects::TreeController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
+ expect(get('/gitlab/gitlabhq/tree/master/files.scss')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
+ end
+end
+
+describe Projects::BlobController, 'routing' do
+ it 'to #edit' do
+ expect(get('/gitlab/gitlabhq/edit/master/app/models/project.rb')).to(
+ route_to('projects/blob#edit',
+ namespace_id: 'gitlab', project_id: 'gitlabhq',
+ id: 'master/app/models/project.rb'))
+ end
+
+ it 'to #preview' do
+ expect(post('/gitlab/gitlabhq/preview/master/app/models/project.rb')).to(
+ route_to('projects/blob#preview',
+ namespace_id: 'gitlab', project_id: 'gitlabhq',
+ id: 'master/app/models/project.rb'))
end
end
# project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/}
# POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/}
# project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/}
-describe Projects::CompareController, "routing" do
- it "to #index" do
- get("/gitlab/gitlabhq/compare").should route_to('projects/compare#index', project_id: 'gitlab/gitlabhq')
+describe Projects::CompareController, 'routing' do
+ it 'to #index' do
+ expect(get('/gitlab/gitlabhq/compare')).to route_to('projects/compare#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #compare" do
- post("/gitlab/gitlabhq/compare").should route_to('projects/compare#create', project_id: 'gitlab/gitlabhq')
+ it 'to #compare' do
+ expect(post('/gitlab/gitlabhq/compare')).to route_to('projects/compare#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
- it "to #show" do
- get("/gitlab/gitlabhq/compare/master...stable").should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'master', to: 'stable')
- get("/gitlab/gitlabhq/compare/issue/1234...stable").should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'issue/1234', to: 'stable')
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/compare/master...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'master', to: 'stable')
+ expect(get('/gitlab/gitlabhq/compare/issue/1234...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable')
end
end
-describe Projects::NetworkController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/network/master").should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master')
- get("/gitlab/gitlabhq/network/master.json").should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master', format: "json")
+describe Projects::NetworkController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/network/master')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
+ expect(get('/gitlab/gitlabhq/network/master.json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
+ end
+end
+
+describe Projects::GraphsController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/graphs/master')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
+ end
+end
+
+describe Projects::ForksController, 'routing' do
+ it 'to #new' do
+ expect(get('/gitlab/gitlabhq/fork/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #create' do
+ expect(post('/gitlab/gitlabhq/fork')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
-describe Projects::GraphsController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/graphs/master").should route_to('projects/graphs#show', project_id: 'gitlab/gitlabhq', id: 'master')
+# project_avatar DELETE /project/avatar(.:format) projects/avatars#destroy
+describe Projects::AvatarsController, 'routing' do
+ it 'to #destroy' do
+ expect(delete('/gitlab/gitlabhq/avatar')).to route_to(
+ 'projects/avatars#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 1e92cf62dd5183e593fa9e092fe9adf5db8b8bd8..d4915b519520e240510600df21d529cdb9c911b6 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
# search GET /search(.:format) search#show
describe SearchController, "routing" do
it "to #show" do
- get("/search").should route_to('search#show')
+ expect(get("/search")).to route_to('search#show')
end
end
@@ -11,11 +11,11 @@ end
# /:path Grack
describe "Mounted Apps", "routing" do
it "to API" do
- get("/api/issues").should be_routable
+ expect(get("/api/issues")).to be_routable
end
it "to Grack" do
- get("/gitlab/gitlabhq.git").should be_routable
+ expect(get("/gitlab/gitlabhq.git")).to be_routable
end
end
@@ -28,39 +28,39 @@ end
# DELETE /snippets/:id(.:format) snippets#destroy
describe SnippetsController, "routing" do
it "to #user_index" do
- get("/s/User").should route_to('snippets#user_index', username: 'User')
+ expect(get("/s/User")).to route_to('snippets#user_index', username: 'User')
end
it "to #raw" do
- get("/snippets/1/raw").should route_to('snippets#raw', id: '1')
+ expect(get("/snippets/1/raw")).to route_to('snippets#raw', id: '1')
end
it "to #index" do
- get("/snippets").should route_to('snippets#index')
+ expect(get("/snippets")).to route_to('snippets#index')
end
it "to #create" do
- post("/snippets").should route_to('snippets#create')
+ expect(post("/snippets")).to route_to('snippets#create')
end
it "to #new" do
- get("/snippets/new").should route_to('snippets#new')
+ expect(get("/snippets/new")).to route_to('snippets#new')
end
it "to #edit" do
- get("/snippets/1/edit").should route_to('snippets#edit', id: '1')
+ expect(get("/snippets/1/edit")).to route_to('snippets#edit', id: '1')
end
it "to #show" do
- get("/snippets/1").should route_to('snippets#show', id: '1')
+ expect(get("/snippets/1")).to route_to('snippets#show', id: '1')
end
it "to #update" do
- put("/snippets/1").should route_to('snippets#update', id: '1')
+ expect(put("/snippets/1")).to route_to('snippets#update', id: '1')
end
it "to #destroy" do
- delete("/snippets/1").should route_to('snippets#destroy', id: '1')
+ expect(delete("/snippets/1")).to route_to('snippets#destroy', id: '1')
end
end
@@ -75,39 +75,39 @@ end
# help_raketasks GET /help/raketasks(.:format) help#raketasks
describe HelpController, "routing" do
it "to #index" do
- get("/help").should route_to('help#index')
+ expect(get("/help")).to route_to('help#index')
end
it "to #permissions" do
- get("/help/permissions/permissions").should route_to('help#show', category: "permissions", file: "permissions")
+ expect(get("/help/permissions/permissions")).to route_to('help#show', category: "permissions", file: "permissions")
end
it "to #workflow" do
- get("/help/workflow/README").should route_to('help#show', category: "workflow", file: "README")
+ expect(get("/help/workflow/README")).to route_to('help#show', category: "workflow", file: "README")
end
it "to #api" do
- get("/help/api/README").should route_to('help#show', category: "api", file: "README")
+ expect(get("/help/api/README")).to route_to('help#show', category: "api", file: "README")
end
it "to #web_hooks" do
- get("/help/web_hooks/web_hooks").should route_to('help#show', category: "web_hooks", file: "web_hooks")
+ expect(get("/help/web_hooks/web_hooks")).to route_to('help#show', category: "web_hooks", file: "web_hooks")
end
it "to #system_hooks" do
- get("/help/system_hooks/system_hooks").should route_to('help#show', category: "system_hooks", file: "system_hooks")
+ expect(get("/help/system_hooks/system_hooks")).to route_to('help#show', category: "system_hooks", file: "system_hooks")
end
it "to #markdown" do
- get("/help/markdown/markdown").should route_to('help#show',category: "markdown", file: "markdown")
+ expect(get("/help/markdown/markdown")).to route_to('help#show',category: "markdown", file: "markdown")
end
it "to #ssh" do
- get("/help/ssh/README").should route_to('help#show', category: "ssh", file: "README")
+ expect(get("/help/ssh/README")).to route_to('help#show', category: "ssh", file: "README")
end
it "to #raketasks" do
- get("/help/raketasks/README").should route_to('help#show', category: "raketasks", file: "README")
+ expect(get("/help/raketasks/README")).to route_to('help#show', category: "raketasks", file: "README")
end
end
@@ -121,23 +121,23 @@ end
# profile_update PUT /profile/update(.:format) profile#update
describe ProfilesController, "routing" do
it "to #account" do
- get("/profile/account").should route_to('profiles/accounts#show')
+ expect(get("/profile/account")).to route_to('profiles/accounts#show')
end
it "to #history" do
- get("/profile/history").should route_to('profiles#history')
+ expect(get("/profile/history")).to route_to('profiles#history')
end
it "to #reset_private_token" do
- put("/profile/reset_private_token").should route_to('profiles#reset_private_token')
+ expect(put("/profile/reset_private_token")).to route_to('profiles#reset_private_token')
end
it "to #show" do
- get("/profile").should route_to('profiles#show')
+ expect(get("/profile")).to route_to('profiles#show')
end
it "to #design" do
- get("/profile/design").should route_to('profiles#design')
+ expect(get("/profile/design")).to route_to('profiles#design')
end
end
@@ -150,36 +150,36 @@ end
# DELETE /keys/:id(.:format) keys#destroy
describe Profiles::KeysController, "routing" do
it "to #index" do
- get("/profile/keys").should route_to('profiles/keys#index')
+ expect(get("/profile/keys")).to route_to('profiles/keys#index')
end
it "to #create" do
- post("/profile/keys").should route_to('profiles/keys#create')
+ expect(post("/profile/keys")).to route_to('profiles/keys#create')
end
it "to #new" do
- get("/profile/keys/new").should route_to('profiles/keys#new')
+ expect(get("/profile/keys/new")).to route_to('profiles/keys#new')
end
it "to #edit" do
- get("/profile/keys/1/edit").should route_to('profiles/keys#edit', id: '1')
+ expect(get("/profile/keys/1/edit")).to route_to('profiles/keys#edit', id: '1')
end
it "to #show" do
- get("/profile/keys/1").should route_to('profiles/keys#show', id: '1')
+ expect(get("/profile/keys/1")).to route_to('profiles/keys#show', id: '1')
end
it "to #update" do
- put("/profile/keys/1").should route_to('profiles/keys#update', id: '1')
+ expect(put("/profile/keys/1")).to route_to('profiles/keys#update', id: '1')
end
it "to #destroy" do
- delete("/profile/keys/1").should route_to('profiles/keys#destroy', id: '1')
+ expect(delete("/profile/keys/1")).to route_to('profiles/keys#destroy', id: '1')
end
# get all the ssh-keys of a user
it "to #get_keys" do
- get("/foo.keys").should route_to('profiles/keys#get_keys', username: 'foo')
+ expect(get("/foo.keys")).to route_to('profiles/keys#get_keys', username: 'foo')
end
end
@@ -188,22 +188,22 @@ end
# DELETE /keys/:id(.:format) keys#destroy
describe Profiles::EmailsController, "routing" do
it "to #index" do
- get("/profile/emails").should route_to('profiles/emails#index')
+ expect(get("/profile/emails")).to route_to('profiles/emails#index')
end
it "to #create" do
- post("/profile/emails").should route_to('profiles/emails#create')
+ expect(post("/profile/emails")).to route_to('profiles/emails#create')
end
it "to #destroy" do
- delete("/profile/emails/1").should route_to('profiles/emails#destroy', id: '1')
+ expect(delete("/profile/emails/1")).to route_to('profiles/emails#destroy', id: '1')
end
end
# profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy
describe Profiles::AvatarsController, "routing" do
it "to #destroy" do
- delete("/profile/avatar").should route_to('profiles/avatars#destroy')
+ expect(delete("/profile/avatar")).to route_to('profiles/avatars#destroy')
end
end
@@ -213,16 +213,16 @@ end
# root / dashboard#show
describe DashboardController, "routing" do
it "to #index" do
- get("/dashboard").should route_to('dashboard#show')
- get("/").should route_to('dashboard#show')
+ expect(get("/dashboard")).to route_to('dashboard#show')
+ expect(get("/")).to route_to('dashboard#show')
end
it "to #issues" do
- get("/dashboard/issues").should route_to('dashboard#issues')
+ expect(get("/dashboard/issues")).to route_to('dashboard#issues')
end
it "to #merge_requests" do
- get("/dashboard/merge_requests").should route_to('dashboard#merge_requests')
+ expect(get("/dashboard/merge_requests")).to route_to('dashboard#merge_requests')
end
end
@@ -241,11 +241,11 @@ end
describe "Groups", "routing" do
it "to #show" do
- get("/groups/1").should route_to('groups#show', id: '1')
+ expect(get("/groups/1")).to route_to('groups#show', id: '1')
end
it "also display group#show on the short path" do
- get('/1').should route_to('namespaces#show', id: '1')
+ expect(get('/1')).to route_to('namespaces#show', id: '1')
end
end
diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..08689c15ca8ac937677691c73bd00bf21834ba80
--- /dev/null
+++ b/spec/services/create_snippet_service_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe CreateSnippetService do
+ before do
+ @user = create :user
+ @admin = create :user, admin: true
+ @opts = {
+ title: 'Test snippet',
+ file_name: 'snippet.rb',
+ content: 'puts "hello world"',
+ visibility_level: Gitlab::VisibilityLevel::PRIVATE
+ }
+ end
+
+ context 'When public visibility is restricted' do
+ before do
+ allow_any_instance_of(ApplicationSetting).to(
+ receive(:restricted_visibility_levels).and_return(
+ [Gitlab::VisibilityLevel::PUBLIC]
+ )
+ )
+
+ @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'non-admins should not be able to create a public snippet' do
+ snippet = create_snippet(nil, @user, @opts)
+ expect(snippet.errors.messages).to have_key(:visibility_level)
+ expect(snippet.errors.messages[:visibility_level].first).to(
+ match('Public visibility has been restricted')
+ )
+ end
+
+ it 'admins should be able to create a public snippet' do
+ snippet = create_snippet(nil, @admin, @opts)
+ expect(snippet.errors.any?).to be_falsey
+ expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+ end
+
+ def create_snippet(project, user, opts)
+ CreateSnippetService.new(project, user, opts).execute
+ end
+end
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index 713aa3e7e747714b25dc9bb5c8c852c28103fa30..007a9eed192e2ea4c11f89a9c83caca139fe966d 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -7,7 +7,7 @@ describe EventCreateService do
describe :open_issue do
let(:issue) { create(:issue) }
- it { service.open_issue(issue, issue.author).should be_true }
+ it { expect(service.open_issue(issue, issue.author)).to be_truthy }
it "should create new event" do
expect { service.open_issue(issue, issue.author) }.to change { Event.count }
@@ -17,7 +17,7 @@ describe EventCreateService do
describe :close_issue do
let(:issue) { create(:issue) }
- it { service.close_issue(issue, issue.author).should be_true }
+ it { expect(service.close_issue(issue, issue.author)).to be_truthy }
it "should create new event" do
expect { service.close_issue(issue, issue.author) }.to change { Event.count }
@@ -27,7 +27,7 @@ describe EventCreateService do
describe :reopen_issue do
let(:issue) { create(:issue) }
- it { service.reopen_issue(issue, issue.author).should be_true }
+ it { expect(service.reopen_issue(issue, issue.author)).to be_truthy }
it "should create new event" do
expect { service.reopen_issue(issue, issue.author) }.to change { Event.count }
@@ -39,7 +39,7 @@ describe EventCreateService do
describe :open_mr do
let(:merge_request) { create(:merge_request) }
- it { service.open_mr(merge_request, merge_request.author).should be_true }
+ it { expect(service.open_mr(merge_request, merge_request.author)).to be_truthy }
it "should create new event" do
expect { service.open_mr(merge_request, merge_request.author) }.to change { Event.count }
@@ -49,7 +49,7 @@ describe EventCreateService do
describe :close_mr do
let(:merge_request) { create(:merge_request) }
- it { service.close_mr(merge_request, merge_request.author).should be_true }
+ it { expect(service.close_mr(merge_request, merge_request.author)).to be_truthy }
it "should create new event" do
expect { service.close_mr(merge_request, merge_request.author) }.to change { Event.count }
@@ -59,7 +59,7 @@ describe EventCreateService do
describe :merge_mr do
let(:merge_request) { create(:merge_request) }
- it { service.merge_mr(merge_request, merge_request.author).should be_true }
+ it { expect(service.merge_mr(merge_request, merge_request.author)).to be_truthy }
it "should create new event" do
expect { service.merge_mr(merge_request, merge_request.author) }.to change { Event.count }
@@ -69,7 +69,7 @@ describe EventCreateService do
describe :reopen_mr do
let(:merge_request) { create(:merge_request) }
- it { service.reopen_mr(merge_request, merge_request.author).should be_true }
+ it { expect(service.reopen_mr(merge_request, merge_request.author)).to be_truthy }
it "should create new event" do
expect { service.reopen_mr(merge_request, merge_request.author) }.to change { Event.count }
@@ -83,7 +83,7 @@ describe EventCreateService do
describe :open_milestone do
let(:milestone) { create(:milestone) }
- it { service.open_milestone(milestone, user).should be_true }
+ it { expect(service.open_milestone(milestone, user)).to be_truthy }
it "should create new event" do
expect { service.open_milestone(milestone, user) }.to change { Event.count }
@@ -93,7 +93,7 @@ describe EventCreateService do
describe :close_mr do
let(:milestone) { create(:milestone) }
- it { service.close_milestone(milestone, user).should be_true }
+ it { expect(service.close_milestone(milestone, user)).to be_truthy }
it "should create new event" do
expect { service.close_milestone(milestone, user) }.to change { Event.count }
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 19b442573f4febf814d949b200e2eeec9c6d9ba9..aa9b15dd9ecc1855b112ccb4274964b44c0e3837 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -20,7 +20,7 @@ describe GitPushService do
service.execute(project, user, @blankrev, @newrev, @ref)
end
- it { should be_true }
+ it { is_expected.to be_truthy }
end
context 'existing branch' do
@@ -28,7 +28,7 @@ describe GitPushService do
service.execute(project, user, @oldrev, @newrev, @ref)
end
- it { should be_true }
+ it { is_expected.to be_truthy }
end
context 'rm branch' do
@@ -36,7 +36,7 @@ describe GitPushService do
service.execute(project, user, @oldrev, @blankrev, @ref)
end
- it { should be_true }
+ it { is_expected.to be_truthy }
end
end
@@ -49,41 +49,54 @@ describe GitPushService do
subject { @push_data }
- it { should include(before: @oldrev) }
- it { should include(after: @newrev) }
- it { should include(ref: @ref) }
- it { should include(user_id: user.id) }
- it { should include(user_name: user.name) }
- it { should include(project_id: project.id) }
+ it { is_expected.to include(object_kind: 'push') }
+ it { is_expected.to include(before: @oldrev) }
+ it { is_expected.to include(after: @newrev) }
+ it { is_expected.to include(ref: @ref) }
+ it { is_expected.to include(user_id: user.id) }
+ it { is_expected.to include(user_name: user.name) }
+ it { is_expected.to include(project_id: project.id) }
context "with repository data" do
subject { @push_data[:repository] }
- it { should include(name: project.name) }
- it { should include(url: project.url_to_repo) }
- it { should include(description: project.description) }
- it { should include(homepage: project.web_url) }
+ it { is_expected.to include(name: project.name) }
+ it { is_expected.to include(url: project.url_to_repo) }
+ it { is_expected.to include(description: project.description) }
+ it { is_expected.to include(homepage: project.web_url) }
end
context "with commits" do
subject { @push_data[:commits] }
- it { should be_an(Array) }
- it { should have(1).element }
+ it { is_expected.to be_an(Array) }
+ it 'has 1 element' do
+ expect(subject.size).to eq(1)
+ end
context "the commit" do
subject { @push_data[:commits].first }
- it { should include(id: @commit.id) }
- it { should include(message: @commit.safe_message) }
- it { should include(timestamp: @commit.date.xmlschema) }
- it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.to_param}/commit/#{@commit.id}") }
+ it { is_expected.to include(id: @commit.id) }
+ it { is_expected.to include(message: @commit.safe_message) }
+ it { is_expected.to include(timestamp: @commit.date.xmlschema) }
+ it do
+ is_expected.to include(
+ url: [
+ Gitlab.config.gitlab.url,
+ project.namespace.to_param,
+ project.to_param,
+ 'commit',
+ @commit.id
+ ].join('/')
+ )
+ end
context "with a author" do
subject { @push_data[:commits].first[:author] }
- it { should include(name: @commit.author_name) }
- it { should include(email: @commit.author_email) }
+ it { is_expected.to include(name: @commit.author_name) }
+ it { is_expected.to include(email: @commit.author_email) }
end
end
end
@@ -95,29 +108,42 @@ describe GitPushService do
@event = Event.last
end
- it { @event.should_not be_nil }
- it { @event.project.should == project }
- it { @event.action.should == Event::PUSHED }
- it { @event.data.should == service.push_data }
+ it { expect(@event).not_to be_nil }
+ it { expect(@event.project).to eq(project) }
+ it { expect(@event.action).to eq(Event::PUSHED) }
+ it { expect(@event.data).to eq(service.push_data) }
end
describe "Web Hooks" do
context "execute web hooks" do
it "when pushing a branch for the first time" do
- project.should_receive(:execute_hooks)
- project.default_branch.should == "master"
- project.protected_branches.should_receive(:create).with({ name: "master" })
+ expect(project).to receive(:execute_hooks)
+ expect(project.default_branch).to eq("master")
+ expect(project.protected_branches).to receive(:create).with({ name: "master", developers_can_push: false })
service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master')
end
- it "when pushing new commits to existing branch" do
- project.should_receive(:execute_hooks)
- service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master')
+ it "when pushing a branch for the first time with default branch protection disabled" do
+ ApplicationSetting.any_instance.stub(default_branch_protection: 0)
+
+ expect(project).to receive(:execute_hooks)
+ expect(project.default_branch).to eq("master")
+ expect(project.protected_branches).not_to receive(:create)
+ service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master')
+ end
+
+ it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do
+ ApplicationSetting.any_instance.stub(default_branch_protection: 1)
+
+ expect(project).to receive(:execute_hooks)
+ expect(project.default_branch).to eq("master")
+ expect(project.protected_branches).to receive(:create).with({ name: "master", developers_can_push: true })
+ service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master')
end
- it "when pushing tags" do
- project.should_not_receive(:execute_hooks)
- service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0')
+ it "when pushing new commits to existing branch" do
+ expect(project).to receive(:execute_hooks)
+ service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master')
end
end
end
@@ -138,7 +164,7 @@ describe GitPushService do
end
it "creates a note if a pushed commit mentions an issue" do
- Note.should_receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
+ expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
service.execute(project, user, @oldrev, @newrev, @ref)
end
@@ -146,35 +172,26 @@ describe GitPushService do
it "only creates a cross-reference note if one doesn't already exist" do
Note.create_cross_reference_note(issue, commit, user, project)
- Note.should_not_receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
+ expect(Note).not_to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
service.execute(project, user, @oldrev, @newrev, @ref)
end
it "defaults to the pushing user if the commit's author is not known" do
commit.stub(author_name: 'unknown name', author_email: 'unknown@email.com')
- Note.should_receive(:create_cross_reference_note).with(issue, commit, user, project)
+ expect(Note).to receive(:create_cross_reference_note).with(issue, commit, user, project)
service.execute(project, user, @oldrev, @newrev, @ref)
end
it "finds references in the first push to a non-default branch" do
- project.repository.stub(:commits_between).with(@blankrev, @newrev).and_return([])
- project.repository.stub(:commits_between).with("master", @newrev).and_return([commit])
+ allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([])
+ allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit])
- Note.should_receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
+ expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
service.execute(project, user, @blankrev, @newrev, 'refs/heads/other')
end
-
- it "finds references in the first push to a default branch" do
- project.repository.stub(:commits_between).with(@blankrev, @newrev).and_return([])
- project.repository.stub(:commits).with(@newrev).and_return([commit])
-
- Note.should_receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
-
- service.execute(project, user, @blankrev, @newrev, 'refs/heads/master')
- end
end
describe "closing issues from pushed commits" do
@@ -197,7 +214,7 @@ describe GitPushService do
it "closes issues with commit messages" do
service.execute(project, user, @oldrev, @newrev, @ref)
- Issue.find(issue.id).should be_closed
+ expect(Issue.find(issue.id)).to be_closed
end
it "doesn't create cross-reference notes for a closing reference" do
@@ -214,7 +231,7 @@ describe GitPushService do
service.execute(project, user, @oldrev, @newrev, 'refs/heads/hurf')
}.not_to change { Note.where(project_id: project.id, system: true).count }
- Issue.find(issue.id).should be_opened
+ expect(Issue.find(issue.id)).to be_opened
end
end
end
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index e65a8204c54ac71d309fcf967844181a12ec619d..a050fdf6c0eb1ac58e08a7243c49d2c76c45db22 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -1,45 +1,87 @@
require 'spec_helper'
describe GitTagPushService do
+ include RepoHelpers
+
let (:user) { create :user }
let (:project) { create :project }
let (:service) { GitTagPushService.new }
before do
- @ref = 'refs/tags/super-tag'
- @oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81'
- @newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb'
+ @oldrev = Gitlab::Git::BLANK_SHA
+ @newrev = "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" # gitlab-test: git rev-parse refs/tags/v1.1.0
+ @ref = 'refs/tags/v1.1.0'
end
- describe 'Git Tag Push Data' do
+ describe "Git Tag Push Data" do
before do
service.execute(project, user, @oldrev, @newrev, @ref)
@push_data = service.push_data
+ @tag_name = Gitlab::Git.ref_name(@ref)
+ @tag = project.repository.find_tag(@tag_name)
+ @commit = project.repository.commit(@tag.target)
end
subject { @push_data }
- it { should include(ref: @ref) }
- it { should include(before: @oldrev) }
- it { should include(after: @newrev) }
- it { should include(user_id: user.id) }
- it { should include(user_name: user.name) }
- it { should include(project_id: project.id) }
+ it { is_expected.to include(object_kind: 'tag_push') }
+ it { is_expected.to include(ref: @ref) }
+ it { is_expected.to include(before: @oldrev) }
+ it { is_expected.to include(after: @newrev) }
+ it { is_expected.to include(message: @tag.message) }
+ it { is_expected.to include(user_id: user.id) }
+ it { is_expected.to include(user_name: user.name) }
+ it { is_expected.to include(project_id: project.id) }
- context 'With repository data' do
+ context "with repository data" do
subject { @push_data[:repository] }
- it { should include(name: project.name) }
- it { should include(url: project.url_to_repo) }
- it { should include(description: project.description) }
- it { should include(homepage: project.web_url) }
+ it { is_expected.to include(name: project.name) }
+ it { is_expected.to include(url: project.url_to_repo) }
+ it { is_expected.to include(description: project.description) }
+ it { is_expected.to include(homepage: project.web_url) }
+ end
+
+ context "with commits" do
+ subject { @push_data[:commits] }
+
+ it { is_expected.to be_an(Array) }
+ it 'has 1 element' do
+ expect(subject.size).to eq(1)
+ end
+
+ context "the commit" do
+ subject { @push_data[:commits].first }
+
+ it { is_expected.to include(id: @commit.id) }
+ it { is_expected.to include(message: @commit.safe_message) }
+ it { is_expected.to include(timestamp: @commit.date.xmlschema) }
+ it do
+ is_expected.to include(
+ url: [
+ Gitlab.config.gitlab.url,
+ project.namespace.to_param,
+ project.to_param,
+ 'commit',
+ @commit.id
+ ].join('/')
+ )
+ end
+
+ context "with a author" do
+ subject { @push_data[:commits].first[:author] }
+
+ it { is_expected.to include(name: @commit.author_name) }
+ it { is_expected.to include(email: @commit.author_email) }
+ end
+ end
end
end
describe "Web Hooks" do
context "execute web hooks" do
it "when pushing tags" do
- project.should_receive(:execute_hooks)
+ expect(project).to receive(:execute_hooks)
service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0')
end
end
diff --git a/spec/services/issues/bulk_update_context_spec.rb b/spec/services/issues/bulk_update_context_spec.rb
deleted file mode 100644
index f4c9148f1a37bcc4e0e74beb51eedd97c50ff07a..0000000000000000000000000000000000000000
--- a/spec/services/issues/bulk_update_context_spec.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'spec_helper'
-
-describe Issues::BulkUpdateService do
- let(:issue) {
- create(:issue, project: @project)
- }
-
- before do
- @user = create :user
- opts = {
- name: "GitLab",
- namespace: @user.namespace
- }
- @project = Projects::CreateService.new(@user, opts).execute
- end
-
- describe :close_issue do
-
- before do
- @issues = 5.times.collect do
- create(:issue, project: @project)
- end
- @params = {
- update: {
- status: 'closed',
- issues_ids: @issues.map(&:id)
- }
- }
- end
-
- it {
- result = Issues::BulkUpdateService.new(@project, @user, @params).execute
- result[:success].should be_true
- result[:count].should == @issues.count
-
- @project.issues.opened.should be_empty
- @project.issues.closed.should_not be_empty
- }
-
- end
-
- describe :reopen_issues do
-
- before do
- @issues = 5.times.collect do
- create(:closed_issue, project: @project)
- end
- @params = {
- update: {
- status: 'reopen',
- issues_ids: @issues.map(&:id)
- }
- }
- end
-
- it {
- result = Issues::BulkUpdateService.new(@project, @user, @params).execute
- result[:success].should be_true
- result[:count].should == @issues.count
-
- @project.issues.closed.should be_empty
- @project.issues.opened.should_not be_empty
- }
-
- end
-
- describe :update_assignee do
-
- before do
- @new_assignee = create :user
- @params = {
- update: {
- issues_ids: [issue.id],
- assignee_id: @new_assignee.id
- }
- }
- end
-
- it {
- result = Issues::BulkUpdateService.new(@project, @user, @params).execute
- result[:success].should be_true
- result[:count].should == 1
-
- @project.issues.first.assignee.should == @new_assignee
- }
-
- end
-
- describe :update_milestone do
-
- before do
- @milestone = create :milestone
- @params = {
- update: {
- issues_ids: [issue.id],
- milestone_id: @milestone.id
- }
- }
- end
-
- it {
- result = Issues::BulkUpdateService.new(@project, @user, @params).execute
- result[:success].should be_true
- result[:count].should == 1
-
- @project.issues.first.milestone.should == @milestone
- }
- end
-
-end
diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a97c55011c91e8a4db4839a925f618eb6143af0f
--- /dev/null
+++ b/spec/services/issues/bulk_update_service_spec.rb
@@ -0,0 +1,121 @@
+require 'spec_helper'
+
+describe Issues::BulkUpdateService do
+ let(:issue) {
+ create(:issue, project: @project)
+ }
+
+ before do
+ @user = create :user
+ opts = {
+ name: "GitLab",
+ namespace: @user.namespace
+ }
+ @project = Projects::CreateService.new(@user, opts).execute
+ end
+
+ describe :close_issue do
+
+ before do
+ @issues = 5.times.collect do
+ create(:issue, project: @project)
+ end
+ @params = {
+ state_event: 'close',
+ issues_ids: @issues.map(&:id)
+ }
+ end
+
+ it {
+ result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(@issues.count)
+
+ expect(@project.issues.opened).to be_empty
+ expect(@project.issues.closed).not_to be_empty
+ }
+
+ end
+
+ describe :reopen_issues do
+
+ before do
+ @issues = 5.times.collect do
+ create(:closed_issue, project: @project)
+ end
+ @params = {
+ state_event: 'reopen',
+ issues_ids: @issues.map(&:id)
+ }
+ end
+
+ it {
+ result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(@issues.count)
+
+ expect(@project.issues.closed).to be_empty
+ expect(@project.issues.opened).not_to be_empty
+ }
+
+ end
+
+ describe :update_assignee do
+
+ before do
+ @new_assignee = create :user
+ @params = {
+ issues_ids: [issue.id],
+ assignee_id: @new_assignee.id
+ }
+ end
+
+ it {
+ result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(1)
+
+ expect(@project.issues.first.assignee).to eq(@new_assignee)
+ }
+
+ it 'allows mass-unassigning' do
+ @project.issues.first.update_attribute(:assignee, @new_assignee)
+ expect(@project.issues.first.assignee).not_to be_nil
+
+ @params[:assignee_id] = -1
+
+ Issues::BulkUpdateService.new(@project, @user, @params).execute
+ expect(@project.issues.first.assignee).to be_nil
+ end
+
+ it 'does not unassign when assignee_id is not present' do
+ @project.issues.first.update_attribute(:assignee, @new_assignee)
+ expect(@project.issues.first.assignee).not_to be_nil
+
+ @params[:assignee_id] = ''
+
+ Issues::BulkUpdateService.new(@project, @user, @params).execute
+ expect(@project.issues.first.assignee).not_to be_nil
+ end
+ end
+
+ describe :update_milestone do
+
+ before do
+ @milestone = create :milestone
+ @params = {
+ issues_ids: [issue.id],
+ milestone_id: @milestone.id
+ }
+ end
+
+ it {
+ result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(1)
+
+ expect(@project.issues.first.milestone).to eq(@milestone)
+ }
+ end
+
+end
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index d4f2cc1339bef8a917e1dacfc8cd325edd3e4f6c..d15dff1b52b24e1f17f8084d238b8151316eaacc 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -17,18 +17,18 @@ describe Issues::CloseService do
@issue = Issues::CloseService.new(project, user, {}).execute(issue)
end
- it { @issue.should be_valid }
- it { @issue.should be_closed }
+ it { expect(@issue).to be_valid }
+ it { expect(@issue).to be_closed }
it 'should send email to user2 about assign of new issue' do
email = ActionMailer::Base.deliveries.last
- email.to.first.should == user2.email
- email.subject.should include(issue.title)
+ expect(email.to.first).to eq(user2.email)
+ expect(email.subject).to include(issue.title)
end
it 'should create system note about issue reassign' do
note = @issue.notes.last
- note.note.should include "Status changed to closed"
+ expect(note.note).to include "Status changed to closed"
end
end
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 90720be5ded3618eca088277014a943151a84ee4..7f1ebcb319897ce69b965524670ddadbc385666d 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -16,8 +16,8 @@ describe Issues::CreateService do
@issue = Issues::CreateService.new(project, user, opts).execute
end
- it { @issue.should be_valid }
- it { @issue.title.should == 'Awesome issue' }
+ it { expect(@issue).to be_valid }
+ it { expect(@issue.title).to eq('Awesome issue') }
end
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 347560414e78bbfb937aa3ada177153c05a84039..22b89bec96d0e032ecf6bdc6e0a404186fe7c008 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -5,6 +5,7 @@ describe Issues::UpdateService do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:issue) { create(:issue) }
+ let(:label) { create(:label) }
before do
project.team << [user, :master]
@@ -18,26 +19,35 @@ describe Issues::UpdateService do
title: 'New title',
description: 'Also please fix',
assignee_id: user2.id,
- state_event: 'close'
+ state_event: 'close',
+ label_ids: [label.id]
}
@issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+ @issue.reload
end
- it { @issue.should be_valid }
- it { @issue.title.should == 'New title' }
- it { @issue.assignee.should == user2 }
- it { @issue.should be_closed }
+ it { expect(@issue).to be_valid }
+ it { expect(@issue.title).to eq('New title') }
+ it { expect(@issue.assignee).to eq(user2) }
+ it { expect(@issue).to be_closed }
+ it { expect(@issue.labels.count).to eq(1) }
+ it { expect(@issue.labels.first.title).to eq('Bug') }
it 'should send email to user2 about assign of new issue' do
email = ActionMailer::Base.deliveries.last
- email.to.first.should == user2.email
- email.subject.should include(issue.title)
+ expect(email.to.first).to eq(user2.email)
+ expect(email.subject).to include(issue.title)
end
it 'should create system note about issue reassign' do
note = @issue.notes.last
- note.note.should include "Reassigned to \@#{user2.username}"
+ expect(note.note).to include "Reassigned to \@#{user2.username}"
+ end
+
+ it 'should create system note about issue label edit' do
+ note = @issue.notes[1]
+ expect(note.note).to include "Added ~#{label.id} label"
end
end
end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index a504f916b08c6eaf17a21636b16ebb4c7318a507..b3cbfd4b5b8d8e9074b186300d10f41191a1a2cd 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -12,23 +12,32 @@ describe MergeRequests::CloseService do
end
describe :execute do
- context "valid params" do
+ context 'valid params' do
+ let(:service) { MergeRequests::CloseService.new(project, user, {}) }
+
before do
- @merge_request = MergeRequests::CloseService.new(project, user, {}).execute(merge_request)
+ allow(service).to receive(:execute_hooks)
+
+ @merge_request = service.execute(merge_request)
end
- it { @merge_request.should be_valid }
- it { @merge_request.should be_closed }
+ it { expect(@merge_request).to be_valid }
+ it { expect(@merge_request).to be_closed }
+
+ it 'should execute hooks with close action' do
+ expect(service).to have_received(:execute_hooks).
+ with(@merge_request, 'close')
+ end
it 'should send email to user2 about assign of new merge_request' do
email = ActionMailer::Base.deliveries.last
- email.to.first.should == user2.email
- email.subject.should include(merge_request.title)
+ expect(email.to.first).to eq(user2.email)
+ expect(email.subject).to include(merge_request.title)
end
it 'should create system note about merge_request reassign' do
note = @merge_request.notes.last
- note.note.should include "Status changed to closed"
+ expect(note.note).to include 'Status changed to closed'
end
end
end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index cebeb0644d0b527aece51263bce35c44149ae3d9..d9bfdf643080752f3ede55ae554b7f590ba8d3c3 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -5,21 +5,30 @@ describe MergeRequests::CreateService do
let(:user) { create(:user) }
describe :execute do
- context "valid params" do
- before do
- project.team << [user, :master]
- opts = {
+ context 'valid params' do
+ let(:opts) do
+ {
title: 'Awesome merge_request',
description: 'please fix',
source_branch: 'stable',
target_branch: 'master'
}
+ end
+ let(:service) { MergeRequests::CreateService.new(project, user, opts) }
+
+ before do
+ project.team << [user, :master]
+ allow(service).to receive(:execute_hooks)
- @merge_request = MergeRequests::CreateService.new(project, user, opts).execute
+ @merge_request = service.execute
end
- it { @merge_request.should be_valid }
- it { @merge_request.title.should == 'Awesome merge_request' }
+ it { expect(@merge_request).to be_valid }
+ it { expect(@merge_request.title).to eq('Awesome merge_request') }
+
+ it 'should execute hooks with default action' do
+ expect(service).to have_received(:execute_hooks).with(@merge_request)
+ end
end
end
end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0a25fb12f4eaa34e964f97349fa7cb5d9ff197f3
--- /dev/null
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe MergeRequests::MergeService do
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:merge_request) { create(:merge_request, assignee: user2) }
+ let(:project) { merge_request.project }
+
+ before do
+ project.team << [user, :master]
+ project.team << [user2, :developer]
+ end
+
+ describe :execute do
+ context 'valid params' do
+ let(:service) { MergeRequests::MergeService.new(project, user, {}) }
+
+ before do
+ allow(service).to receive(:execute_hooks)
+
+ service.execute(merge_request, 'Awesome message')
+ end
+
+ it { expect(merge_request).to be_valid }
+ it { expect(merge_request).to be_merged }
+
+ it 'should execute hooks with merge action' do
+ expect(service).to have_received(:execute_hooks).
+ with(merge_request, 'merge')
+ end
+
+ it 'should send email to user2 about merge of new merge_request' do
+ email = ActionMailer::Base.deliveries.last
+ expect(email.to.first).to eq(user2.email)
+ expect(email.subject).to include(merge_request.title)
+ end
+
+ it 'should create system note about merge_request merge' do
+ note = merge_request.notes.last
+ expect(note.note).to include 'Status changed to merged'
+ end
+ end
+ end
+end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 9f2941520532aef3e771aa60b742809f7a40066e..879df0c9c67655bfcf7880aab8c7e5cffe8bc0ac 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -35,10 +35,10 @@ describe MergeRequests::RefreshService do
reload_mrs
end
- it { @merge_request.notes.should_not be_empty }
- it { @merge_request.should be_open }
- it { @fork_merge_request.should be_open }
- it { @fork_merge_request.notes.should be_empty }
+ it { expect(@merge_request.notes).not_to be_empty }
+ it { expect(@merge_request).to be_open }
+ it { expect(@fork_merge_request).to be_open }
+ it { expect(@fork_merge_request.notes).to be_empty }
end
context 'push to origin repo target branch' do
@@ -47,10 +47,10 @@ describe MergeRequests::RefreshService do
reload_mrs
end
- it { @merge_request.notes.should be_empty }
- it { @merge_request.should be_merged }
- it { @fork_merge_request.should be_merged }
- it { @fork_merge_request.notes.should be_empty }
+ it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request).to be_merged }
+ it { expect(@fork_merge_request).to be_merged }
+ it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
end
context 'push to fork repo source branch' do
@@ -59,10 +59,10 @@ describe MergeRequests::RefreshService do
reload_mrs
end
- it { @merge_request.notes.should be_empty }
- it { @merge_request.should be_open }
- it { @fork_merge_request.notes.should_not be_empty }
- it { @fork_merge_request.should be_open }
+ it { expect(@merge_request.notes).to be_empty }
+ it { expect(@merge_request).to be_open }
+ it { expect(@fork_merge_request.notes.last.note).to include('Added 4 commits') }
+ it { expect(@fork_merge_request).to be_open }
end
context 'push to fork repo target branch' do
@@ -71,10 +71,10 @@ describe MergeRequests::RefreshService do
reload_mrs
end
- it { @merge_request.notes.should be_empty }
- it { @merge_request.should be_open }
- it { @fork_merge_request.notes.should be_empty }
- it { @fork_merge_request.should be_open }
+ it { expect(@merge_request.notes).to be_empty }
+ it { expect(@merge_request).to be_open }
+ it { expect(@fork_merge_request.notes).to be_empty }
+ it { expect(@fork_merge_request).to be_open }
end
context 'push to origin repo target branch after fork project was removed' do
@@ -84,10 +84,10 @@ describe MergeRequests::RefreshService do
reload_mrs
end
- it { @merge_request.notes.should be_empty }
- it { @merge_request.should be_merged }
- it { @fork_merge_request.should be_open }
- it { @fork_merge_request.notes.should be_empty }
+ it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request).to be_merged }
+ it { expect(@fork_merge_request).to be_open }
+ it { expect(@fork_merge_request.notes).to be_empty }
end
def reload_mrs
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9401bc3b55893ab47e734083b10d2dbd6cead703
--- /dev/null
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+describe MergeRequests::ReopenService do
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:merge_request) { create(:merge_request, assignee: user2) }
+ let(:project) { merge_request.project }
+
+ before do
+ project.team << [user, :master]
+ project.team << [user2, :developer]
+ end
+
+ describe :execute do
+ context 'valid params' do
+ let(:service) { MergeRequests::ReopenService.new(project, user, {}) }
+
+ before do
+ allow(service).to receive(:execute_hooks)
+
+ merge_request.state = :closed
+ service.execute(merge_request)
+ end
+
+ it { expect(merge_request).to be_valid }
+ it { expect(merge_request).to be_reopened }
+
+ it 'should execute hooks with reopen action' do
+ expect(service).to have_received(:execute_hooks).
+ with(merge_request, 'reopen')
+ end
+
+ it 'should send email to user2 about reopen of merge_request' do
+ email = ActionMailer::Base.deliveries.last
+ expect(email.to.first).to eq(user2.email)
+ expect(email.subject).to include(merge_request.title)
+ end
+
+ it 'should create system note about merge_request reopen' do
+ note = merge_request.notes.last
+ expect(note.note).to include 'Status changed to reopened'
+ end
+ end
+ end
+end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index af5d3a3dc817e0f9a727c15cc0e08dfd1a73141a..916b01e1c45e4a84403f52626623dd529ab9837e 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -5,6 +5,7 @@ describe MergeRequests::UpdateService do
let(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, :simple) }
let(:project) { merge_request.project }
+ let(:label) { create(:label) }
before do
project.team << [user, :master]
@@ -12,32 +13,52 @@ describe MergeRequests::UpdateService do
end
describe :execute do
- context "valid params" do
- before do
- opts = {
+ context 'valid params' do
+ let(:opts) do
+ {
title: 'New title',
description: 'Also please fix',
assignee_id: user2.id,
- state_event: 'close'
+ state_event: 'close',
+ label_ids: [label.id]
}
+ end
+
+ let(:service) { MergeRequests::UpdateService.new(project, user, opts) }
+
+ before do
+ allow(service).to receive(:execute_hooks)
- @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+ @merge_request = service.execute(merge_request)
+ @merge_request.reload
end
- it { @merge_request.should be_valid }
- it { @merge_request.title.should == 'New title' }
- it { @merge_request.assignee.should == user2 }
- it { @merge_request.should be_closed }
+ it { expect(@merge_request).to be_valid }
+ it { expect(@merge_request.title).to eq('New title') }
+ it { expect(@merge_request.assignee).to eq(user2) }
+ it { expect(@merge_request).to be_closed }
+ it { expect(@merge_request.labels.count).to eq(1) }
+ it { expect(@merge_request.labels.first.title).to eq('Bug') }
+
+ it 'should execute hooks with update action' do
+ expect(service).to have_received(:execute_hooks).
+ with(@merge_request, 'update')
+ end
it 'should send email to user2 about assign of new merge_request' do
email = ActionMailer::Base.deliveries.last
- email.to.first.should == user2.email
- email.subject.should include(merge_request.title)
+ expect(email.to.first).to eq(user2.email)
+ expect(email.subject).to include(merge_request.title)
end
it 'should create system note about merge_request reassign' do
note = @merge_request.notes.last
- note.note.should include "Reassigned to \@#{user2.username}"
+ expect(note.note).to include "Reassigned to \@#{user2.username}"
+ end
+
+ it 'should create system note about merge_request label edit' do
+ note = @merge_request.notes[1]
+ expect(note.note).to include "Added ~#{label.id} label"
end
end
end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index f59786efcf917266ee30b4ec0f934009c3f115a9..1a02299bf19c5f4b247d134b56c589396767ca43 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -18,8 +18,8 @@ describe Notes::CreateService do
@note = Notes::CreateService.new(project, user, opts).execute
end
- it { @note.should be_valid }
- it { @note.note.should == 'Awesome comment' }
+ it { expect(@note).to be_valid }
+ it { expect(@note.note).to eq('Awesome comment') }
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index f8377650e0a6193a2200f93868e4850d55bbf248..bfca2c88264354f7aca3f3abd5c06579ec18ccc0 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -7,10 +7,10 @@ describe NotificationService do
describe :new_key do
let!(:key) { create(:personal_key) }
- it { notification.new_key(key).should be_true }
+ it { expect(notification.new_key(key)).to be_truthy }
it 'should sent email to key owner' do
- Notify.should_receive(:new_ssh_key_email).with(key.id)
+ expect(Notify).to receive(:new_ssh_key_email).with(key.id)
notification.new_key(key)
end
end
@@ -20,10 +20,10 @@ describe NotificationService do
describe :new_email do
let!(:email) { create(:email) }
- it { notification.new_email(email).should be_true }
+ it { expect(notification.new_email(email)).to be_truthy }
it 'should send email to email owner' do
- Notify.should_receive(:new_email_email).with(email.id)
+ expect(Notify).to receive(:new_email_email).with(email.id)
notification.new_email(email)
end
end
@@ -41,20 +41,25 @@ describe NotificationService do
describe :new_note do
it do
+ add_users_with_subscription(note.project, issue)
+
should_email(@u_watcher.id)
should_email(note.noteable.author_id)
should_email(note.noteable.assignee_id)
should_email(@u_mentioned.id)
+ should_email(@subscriber.id)
should_not_email(note.author_id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
+ should_not_email(@unsubscriber.id)
+
notification.new_note(note)
end
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
- Notify.should_not_receive(:note_issue_email)
+ expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
end
end
@@ -69,9 +74,9 @@ describe NotificationService do
user_project = note.project.project_members.find_by_user_id(@u_watcher.id)
user_project.notification_level = Notification::N_PARTICIPATING
user_project.save
- user_group = note.project.group.group_members.find_by_user_id(@u_watcher.id)
- user_group.notification_level = Notification::N_GLOBAL
- user_group.save
+ group_member = note.project.group.group_members.find_by_user_id(@u_watcher.id)
+ group_member.notification_level = Notification::N_GLOBAL
+ group_member.save
end
it do
@@ -87,11 +92,11 @@ describe NotificationService do
end
def should_email(user_id)
- Notify.should_receive(:note_issue_email).with(user_id, note.id)
+ expect(Notify).to receive(:note_issue_email).with(user_id, note.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:note_issue_email).with(user_id, note.id)
+ expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id)
end
end
@@ -116,6 +121,7 @@ describe NotificationService do
should_email(note.noteable.assignee_id)
should_not_email(note.author_id)
+ should_not_email(@u_mentioned.id)
should_not_email(@u_disabled.id)
should_not_email(@u_not_mentioned.id)
notification.new_note(note)
@@ -124,17 +130,17 @@ describe NotificationService do
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
- Notify.should_not_receive(:note_issue_email)
+ expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
end
end
def should_email(user_id)
- Notify.should_receive(:note_issue_email).with(user_id, note.id)
+ expect(Notify).to receive(:note_issue_email).with(user_id, note.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:note_issue_email).with(user_id, note.id)
+ expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id)
end
end
@@ -168,39 +174,54 @@ describe NotificationService do
notification.new_note(note)
end
+ it do
+ @u_committer.update_attributes(notification_level: Notification::N_MENTION)
+ should_not_email(@u_committer.id, note)
+ notification.new_note(note)
+ end
+
def should_email(user_id, n)
- Notify.should_receive(:note_commit_email).with(user_id, n.id)
+ expect(Notify).to receive(:note_commit_email).with(user_id, n.id)
end
def should_not_email(user_id, n)
- Notify.should_not_receive(:note_commit_email).with(user_id, n.id)
+ expect(Notify).not_to receive(:note_commit_email).with(user_id, n.id)
end
end
end
end
describe 'Issues' do
- let(:issue) { create :issue, assignee: create(:user) }
+ let(:issue) { create :issue, assignee: create(:user), description: 'cc @participant' }
before do
build_team(issue.project)
+ add_users_with_subscription(issue.project, issue)
end
describe :new_issue do
it do
should_email(issue.assignee_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
+ should_not_email(@u_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
notification.new_issue(issue, @u_disabled)
end
+ it do
+ issue.assignee.update_attributes(notification_level: Notification::N_MENTION)
+ should_not_email(issue.assignee_id)
+ notification.new_issue(issue, @u_disabled)
+ end
+
def should_email(user_id)
- Notify.should_receive(:new_issue_email).with(user_id, issue.id)
+ expect(Notify).to receive(:new_issue_email).with(user_id, issue.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:new_issue_email).with(user_id, issue.id)
+ expect(Notify).not_to receive(:new_issue_email).with(user_id, issue.id)
end
end
@@ -208,6 +229,9 @@ describe NotificationService do
it 'should email new assignee' do
should_email(issue.assignee_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -215,11 +239,11 @@ describe NotificationService do
end
def should_email(user_id)
- Notify.should_receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id)
+ expect(Notify).to receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id)
+ expect(Notify).not_to receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id)
end
end
@@ -228,6 +252,9 @@ describe NotificationService do
should_email(issue.assignee_id)
should_email(issue.author_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -235,11 +262,11 @@ describe NotificationService do
end
def should_email(user_id)
- Notify.should_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id)
+ expect(Notify).to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id)
+ expect(Notify).not_to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id)
end
end
@@ -248,6 +275,9 @@ describe NotificationService do
should_email(issue.assignee_id)
should_email(issue.author_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -255,11 +285,11 @@ describe NotificationService do
end
def should_email(user_id)
- Notify.should_receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id)
+ expect(Notify).to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id)
+ expect(Notify).not_to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id)
end
end
end
@@ -269,6 +299,7 @@ describe NotificationService do
before do
build_team(merge_request.target_project)
+ add_users_with_subscription(merge_request.target_project, merge_request)
end
describe :new_merge_request do
@@ -281,11 +312,11 @@ describe NotificationService do
end
def should_email(user_id)
- Notify.should_receive(:new_merge_request_email).with(user_id, merge_request.id)
+ expect(Notify).to receive(:new_merge_request_email).with(user_id, merge_request.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:new_merge_request_email).with(user_id, merge_request.id)
+ expect(Notify).not_to receive(:new_merge_request_email).with(user_id, merge_request.id)
end
end
@@ -293,17 +324,19 @@ describe NotificationService do
it do
should_email(merge_request.assignee_id)
should_email(@u_watcher.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
notification.reassigned_merge_request(merge_request, merge_request.author)
end
def should_email(user_id)
- Notify.should_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id)
+ expect(Notify).to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id)
+ expect(Notify).not_to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id)
end
end
@@ -311,17 +344,19 @@ describe NotificationService do
it do
should_email(merge_request.assignee_id)
should_email(@u_watcher.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
notification.close_mr(merge_request, @u_disabled)
end
def should_email(user_id)
- Notify.should_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
+ expect(Notify).to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
+ expect(Notify).not_to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end
end
@@ -329,17 +364,19 @@ describe NotificationService do
it do
should_email(merge_request.assignee_id)
should_email(@u_watcher.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
notification.merge_mr(merge_request, @u_disabled)
end
def should_email(user_id)
- Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
+ expect(Notify).to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
+ expect(Notify).not_to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end
end
@@ -347,17 +384,19 @@ describe NotificationService do
it do
should_email(merge_request.assignee_id)
should_email(@u_watcher.id)
+ should_email(@subscriber.id)
+ should_not_email(@unsubscriber.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
notification.reopen_mr(merge_request, @u_disabled)
end
def should_email(user_id)
- Notify.should_receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id)
+ expect(Notify).to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id)
+ expect(Notify).not_to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id)
end
end
end
@@ -378,11 +417,11 @@ describe NotificationService do
end
def should_email(user_id)
- Notify.should_receive(:project_was_moved_email).with(project.id, user_id)
+ expect(Notify).to receive(:project_was_moved_email).with(project.id, user_id)
end
def should_not_email(user_id)
- Notify.should_not_receive(:project_was_moved_email).with(project.id, user_id)
+ expect(Notify).not_to receive(:project_was_moved_email).with(project.id, user_id)
end
end
end
@@ -390,8 +429,9 @@ describe NotificationService do
def build_team(project)
@u_watcher = create(:user, notification_level: Notification::N_WATCH)
@u_participating = create(:user, notification_level: Notification::N_PARTICIPATING)
+ @u_participant_mentioned = create(:user, username: 'participant', notification_level: Notification::N_PARTICIPATING)
@u_disabled = create(:user, notification_level: Notification::N_DISABLED)
- @u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_PARTICIPATING)
+ @u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_MENTION)
@u_committer = create(:user, username: 'committer')
@u_not_mentioned = create(:user, username: 'regular', notification_level: Notification::N_PARTICIPATING)
@@ -401,4 +441,15 @@ describe NotificationService do
project.team << [@u_mentioned, :master]
project.team << [@u_committer, :master]
end
+
+ def add_users_with_subscription(project, issuable)
+ @subscriber = create :user
+ @unsubscriber = create :user
+
+ project.team << [@subscriber, :master]
+ project.team << [@unsubscriber, :master]
+
+ issuable.subscriptions.create(user: @subscriber, subscribed: true)
+ issuable.subscriptions.create(user: @unsubscriber, subscribed: false)
+ end
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 9c97dad2ff014b2d7499307bf71516a4fc4927ce..337dae592dd488024653093b2c918ec94880c2bc 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -16,9 +16,9 @@ describe Projects::CreateService do
@project = create_project(@user, @opts)
end
- it { @project.should be_valid }
- it { @project.owner.should == @user }
- it { @project.namespace.should == @user.namespace }
+ it { expect(@project).to be_valid }
+ it { expect(@project.owner).to eq(@user) }
+ it { expect(@project.namespace).to eq(@user.namespace) }
end
context 'group namespace' do
@@ -30,9 +30,9 @@ describe Projects::CreateService do
@project = create_project(@user, @opts)
end
- it { @project.should be_valid }
- it { @project.owner.should == @group }
- it { @project.namespace.should == @group }
+ it { expect(@project).to be_valid }
+ it { expect(@project.owner).to eq(@group) }
+ it { expect(@project.namespace).to eq(@group) }
end
context 'wiki_enabled creates repository directory' do
@@ -42,7 +42,7 @@ describe Projects::CreateService do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { File.exists?(@path).should be_true }
+ it { expect(File.exists?(@path)).to be_truthy }
end
context 'wiki_enabled false does not create wiki repository directory' do
@@ -52,7 +52,34 @@ describe Projects::CreateService do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { File.exists?(@path).should be_false }
+ it { expect(File.exists?(@path)).to be_falsey }
+ end
+ end
+
+ context 'restricted visibility level' do
+ before do
+ allow_any_instance_of(ApplicationSetting).to(
+ receive(:restricted_visibility_levels).and_return([20])
+ )
+
+ @opts.merge!(
+ visibility_level: Gitlab::VisibilityLevel.options['Public']
+ )
+ end
+
+ it 'should not allow a restricted visibility level for non-admins' do
+ project = create_project(@user, @opts)
+ expect(project).to respond_to(:errors)
+ expect(project.errors.messages).to have_key(:visibility_level)
+ expect(project.errors.messages[:visibility_level].first).to(
+ match('restricted by your GitLab administrator')
+ )
+ end
+
+ it 'should allow a restricted visibility level for admins' do
+ project = create_project(@admin, @opts)
+ expect(project.errors.any?).to be(false)
+ expect(project.saved?).to be(true)
end
end
end
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 0edc3a8e8076846477ad7938dee5ae9bfb37fc1c..e55a2e3f8a0d8c5f1ac6f1194a014b9a8cc1bcbb 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -16,18 +16,18 @@ describe Projects::ForkService do
describe "successfully creates project in the user namespace" do
let(:to_project) { fork_project(@from_project, @to_user) }
- it { to_project.owner.should == @to_user }
- it { to_project.namespace.should == @to_user.namespace }
- it { to_project.star_count.should be_zero }
- it { to_project.description.should == @from_project.description }
+ it { expect(to_project.owner).to eq(@to_user) }
+ it { expect(to_project.namespace).to eq(@to_user.namespace) }
+ it { expect(to_project.star_count).to be_zero }
+ it { expect(to_project.description).to eq(@from_project.description) }
end
end
context 'fork project failure' do
it "fails due to transaction failure" do
@to_project = fork_project(@from_project, @to_user, false)
- @to_project.errors.should_not be_empty
- @to_project.errors[:base].should include("Fork transaction failed.")
+ expect(@to_project.errors).not_to be_empty
+ expect(@to_project.errors[:base]).to include("Fork transaction failed.")
end
end
@@ -35,17 +35,61 @@ describe Projects::ForkService do
it "should fail due to validation, not transaction failure" do
@existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace)
@to_project = fork_project(@from_project, @to_user)
- @existing_project.persisted?.should be_true
- @to_project.errors[:base].should include("Invalid fork destination")
- @to_project.errors[:base].should_not include("Fork transaction failed.")
+ expect(@existing_project.persisted?).to be_truthy
+ expect(@to_project.errors[:base]).to include("Invalid fork destination")
+ expect(@to_project.errors[:base]).not_to include("Fork transaction failed.")
end
end
end
- def fork_project(from_project, user, fork_success = true)
- context = Projects::ForkService.new(from_project, user)
- shell = double("gitlab_shell")
- shell.stub(fork_repository: fork_success)
+ describe :fork_to_namespace do
+ before do
+ @group_owner = create(:user)
+ @developer = create(:user)
+ @project = create(:project, creator_id: @group_owner.id,
+ star_count: 777,
+ description: 'Wow, such a cool project!')
+ @group = create(:group)
+ @group.add_user(@group_owner, GroupMember::OWNER)
+ @group.add_user(@developer, GroupMember::DEVELOPER)
+ @opts = { namespace: @group }
+ end
+
+ context 'fork project for group' do
+ it 'group owner successfully forks project into the group' do
+ to_project = fork_project(@project, @group_owner, true, @opts)
+ expect(to_project.owner).to eq(@group)
+ expect(to_project.namespace).to eq(@group)
+ expect(to_project.name).to eq(@project.name)
+ expect(to_project.path).to eq(@project.path)
+ expect(to_project.description).to eq(@project.description)
+ expect(to_project.star_count).to be_zero
+ end
+ end
+
+ context 'fork project for group when user not owner' do
+ it 'group developer should fail to fork project into the group' do
+ to_project = fork_project(@project, @developer, true, @opts)
+ expect(to_project.errors[:namespace]).to eq(['insufficient access rights'])
+ end
+ end
+
+ context 'project already exists in group' do
+ it 'should fail due to validation, not transaction failure' do
+ existing_project = create(:project, name: @project.name,
+ namespace: @group)
+ to_project = fork_project(@project, @group_owner, true, @opts)
+ expect(existing_project.persisted?).to be_truthy
+ expect(to_project.errors[:base]).to eq(['Invalid fork destination'])
+ expect(to_project.errors[:name]).to eq(['has already been taken'])
+ expect(to_project.errors[:path]).to eq(['has already been taken'])
+ end
+ end
+ end
+
+ def fork_project(from_project, user, fork_success = true, params = {})
+ context = Projects::ForkService.new(from_project, user, params)
+ shell = double('gitlab_shell').stub(fork_repository: fork_success)
context.stub(gitlab_shell: shell)
context.execute
end
diff --git a/spec/services/projects/image_service_spec.rb b/spec/services/projects/image_service_spec.rb
deleted file mode 100644
index 23c4e227ae303ed6534eeb591650ba3e1b7cc6f5..0000000000000000000000000000000000000000
--- a/spec/services/projects/image_service_spec.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'spec_helper'
-
-describe Projects::ImageService do
- describe 'Image service' do
- before do
- @user = create :user
- @project = create :project, creator_id: @user.id, namespace: @user.namespace
- end
-
- context 'for valid gif file' do
- before do
- gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
- @link_to_image = upload_image(@project.repository, { 'markdown_img' => gif }, "http://test.example/")
- end
-
- it { expect(@link_to_image).to have_key("alt") }
- it { expect(@link_to_image).to have_key("url") }
- it { expect(@link_to_image).to have_value("banana_sample") }
- it { expect(@link_to_image["url"]).to match("http://test.example/uploads/#{@project.path_with_namespace}") }
- it { expect(@link_to_image["url"]).to match("banana_sample.gif") }
- end
-
- context 'for valid png file' do
- before do
- png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png')
- @link_to_image = upload_image(@project.repository, { 'markdown_img' => png }, "http://test.example/")
- end
-
- it { expect(@link_to_image).to have_key("alt") }
- it { expect(@link_to_image).to have_key("url") }
- it { expect(@link_to_image).to have_value("dk") }
- it { expect(@link_to_image["url"]).to match("http://test.example/uploads/#{@project.path_with_namespace}") }
- it { expect(@link_to_image["url"]).to match("dk.png") }
- end
-
- context 'for valid jpg file' do
- before do
- jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg')
- @link_to_image = upload_image(@project.repository, { 'markdown_img' => jpg }, "http://test.example/")
- end
-
- it { expect(@link_to_image).to have_key("alt") }
- it { expect(@link_to_image).to have_key("url") }
- it { expect(@link_to_image).to have_value("rails_sample") }
- it { expect(@link_to_image["url"]).to match("http://test.example/uploads/#{@project.path_with_namespace}") }
- it { expect(@link_to_image["url"]).to match("rails_sample.jpg") }
- end
-
- context 'for txt file' do
- before do
- txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain')
- @link_to_image = upload_image(@project.repository, { 'markdown_img' => txt }, "http://test.example/")
- end
-
- it { expect(@link_to_image).to be_nil }
- end
- end
-
- def upload_image(repository, params, root_url)
- Projects::ImageService.new(repository, params, root_url).execute
- end
-end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 79d0526ff89cb3b1d2a8793a3b456f304b2a426e..5650626fb18428cc082776a9bbb6169da491ebfb 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -8,31 +8,31 @@ describe Projects::TransferService do
context 'namespace -> namespace' do
before do
group.add_owner(user)
- @result = transfer_project(project, user, namespace_id: group.id)
+ @result = transfer_project(project, user, new_namespace_id: group.id)
end
- it { @result.should be_true }
- it { project.namespace.should == group }
+ it { expect(@result).to be_truthy }
+ it { expect(project.namespace).to eq(group) }
end
context 'namespace -> no namespace' do
before do
- @result = transfer_project(project, user, namespace_id: nil)
+ @result = transfer_project(project, user, new_namespace_id: nil)
end
- it { @result.should_not be_nil } # { result.should be_false } passes on nil
- it { @result.should be_false }
- it { project.namespace.should == user.namespace }
+ it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil
+ it { expect(@result).to be_falsey }
+ it { expect(project.namespace).to eq(user.namespace) }
end
context 'namespace -> not allowed namespace' do
before do
- @result = transfer_project(project, user, namespace_id: group.id)
+ @result = transfer_project(project, user, new_namespace_id: group.id)
end
- it { @result.should_not be_nil } # { result.should be_false } passes on nil
- it { @result.should be_false }
- it { project.namespace.should == user.namespace }
+ it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil
+ it { expect(@result).to be_falsey }
+ it { expect(project.namespace).to eq(user.namespace) }
end
def transfer_project(project, user, params)
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 5a10174eb36b6ee52b4b9a52c4380ec08c181b24..ea5b8813105ca7f1a7b6f4585e2ebb2c8a341ac3 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -17,8 +17,8 @@ describe Projects::UpdateService do
update_project(@project, @user, @opts)
end
- it { @created_private.should be_true }
- it { @project.private?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.private?).to be_truthy }
end
context 'should be internal when updated to internal' do
@@ -29,8 +29,8 @@ describe Projects::UpdateService do
update_project(@project, @user, @opts)
end
- it { @created_private.should be_true }
- it { @project.internal?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.internal?).to be_truthy }
end
context 'should be public when updated to public' do
@@ -41,15 +41,15 @@ describe Projects::UpdateService do
update_project(@project, @user, @opts)
end
- it { @created_private.should be_true }
- it { @project.public?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.public?).to be_truthy }
end
context 'respect configured visibility restrictions setting' do
before(:each) do
- @restrictions = double("restrictions")
- @restrictions.stub(:restricted_visibility_levels) { [ "public" ] }
- Settings.stub_chain(:gitlab).and_return(@restrictions)
+ allow_any_instance_of(ApplicationSetting).to(
+ receive(:restricted_visibility_levels).and_return([20])
+ )
end
context 'should be private when updated to private' do
@@ -60,8 +60,8 @@ describe Projects::UpdateService do
update_project(@project, @user, @opts)
end
- it { @created_private.should be_true }
- it { @project.private?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.private?).to be_truthy }
end
context 'should be internal when updated to internal' do
@@ -72,8 +72,8 @@ describe Projects::UpdateService do
update_project(@project, @user, @opts)
end
- it { @created_private.should be_true }
- it { @project.internal?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.internal?).to be_truthy }
end
context 'should be private when updated to public' do
@@ -84,8 +84,8 @@ describe Projects::UpdateService do
update_project(@project, @user, @opts)
end
- it { @created_private.should be_true }
- it { @project.private?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.private?).to be_truthy }
end
context 'should be public when updated to public by admin' do
@@ -96,8 +96,8 @@ describe Projects::UpdateService do
update_project(@project, @admin, @opts)
end
- it { @created_private.should be_true }
- it { @project.public?.should be_true }
+ it { expect(@created_private).to be_truthy }
+ it { expect(@project.public?).to be_truthy }
end
end
end
diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fc34b45648262fd4345d04aa6e4ba59cc913f24b
--- /dev/null
+++ b/spec/services/projects/upload_service_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe Projects::UploadService do
+ describe 'File service' do
+ before do
+ @user = create :user
+ @project = create :project, creator_id: @user.id, namespace: @user.namespace
+ end
+
+ context 'for valid gif file' do
+ before do
+ gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
+ @link_to_file = upload_file(@project.repository, gif)
+ end
+
+ it { expect(@link_to_file).to have_key('alt') }
+ it { expect(@link_to_file).to have_key('url') }
+ it { expect(@link_to_file).to have_key('is_image') }
+ it { expect(@link_to_file).to have_value('banana_sample') }
+ it { expect(@link_to_file['is_image']).to equal(true) }
+ it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
+ it { expect(@link_to_file['url']).to match('banana_sample.gif') }
+ end
+
+ context 'for valid png file' do
+ before do
+ png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png',
+ 'image/png')
+ @link_to_file = upload_file(@project.repository, png)
+ end
+
+ it { expect(@link_to_file).to have_key('alt') }
+ it { expect(@link_to_file).to have_key('url') }
+ it { expect(@link_to_file).to have_value('dk') }
+ it { expect(@link_to_file).to have_key('is_image') }
+ it { expect(@link_to_file['is_image']).to equal(true) }
+ it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
+ it { expect(@link_to_file['url']).to match('dk.png') }
+ end
+
+ context 'for valid jpg file' do
+ before do
+ jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg')
+ @link_to_file = upload_file(@project.repository, jpg)
+ end
+
+ it { expect(@link_to_file).to have_key('alt') }
+ it { expect(@link_to_file).to have_key('url') }
+ it { expect(@link_to_file).to have_key('is_image') }
+ it { expect(@link_to_file).to have_value('rails_sample') }
+ it { expect(@link_to_file['is_image']).to equal(true) }
+ it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
+ it { expect(@link_to_file['url']).to match('rails_sample.jpg') }
+ end
+
+ context 'for txt file' do
+ before do
+ txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain')
+ @link_to_file = upload_file(@project.repository, txt)
+ end
+
+ it { expect(@link_to_file).to have_key('alt') }
+ it { expect(@link_to_file).to have_key('url') }
+ it { expect(@link_to_file).to have_key('is_image') }
+ it { expect(@link_to_file).to have_value('doc_sample.txt') }
+ it { expect(@link_to_file['is_image']).to equal(false) }
+ it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
+ it { expect(@link_to_file['url']).to match('doc_sample.txt') }
+ end
+ end
+
+ def upload_file(repository, file)
+ Projects::UploadService.new(repository, file).execute
+ end
+end
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 3217c571e67a6072cf0ab97bfd8813cce09b24aa..f57bfaea8798f19d0ca7fe145494324b8fd73c3b 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -19,7 +19,7 @@ describe 'Search::GlobalService' do
it 'should return public projects only' do
context = Search::GlobalService.new(nil, search: "searchable")
results = context.execute
- results.objects('projects').should match_array [public_project]
+ expect(results.objects('projects')).to match_array [public_project]
end
end
@@ -27,19 +27,19 @@ describe 'Search::GlobalService' do
it 'should return public, internal and private projects' do
context = Search::GlobalService.new(user, search: "searchable")
results = context.execute
- results.objects('projects').should match_array [public_project, found_project, internal_project]
+ expect(results.objects('projects')).to match_array [public_project, found_project, internal_project]
end
it 'should return only public & internal projects' do
context = Search::GlobalService.new(internal_user, search: "searchable")
results = context.execute
- results.objects('projects').should match_array [internal_project, public_project]
+ expect(results.objects('projects')).to match_array [internal_project, public_project]
end
it 'namespace name should be searchable' do
context = Search::GlobalService.new(user, search: found_project.namespace.path)
results = context.execute
- results.objects('projects').should match_array [found_project]
+ expect(results.objects('projects')).to match_array [found_project]
end
end
end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 573446d3a190dab2c564ee60ea7a711f2af2fd69..199ac9966087ae5ed2162d856cc194a5e3b96765 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -5,27 +5,58 @@ describe SystemHooksService do
let (:project) { create :project }
let (:project_member) { create :project_member }
let (:key) { create(:key, user: user) }
+ let (:group) { create(:group) }
+ let (:group_member) { create(:group_member) }
context 'event data' do
- it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) }
- it { event_data(user, :destroy).should include(:event_name, :name, :created_at, :email, :user_id) }
- it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
- it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
- it { event_data(project_member, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
- it { event_data(project_member, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
- it { event_data(key, :create).should include(:username, :key, :id) }
- it { event_data(key, :destroy).should include(:username, :key, :id) }
+ it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :email, :user_id) }
+ it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :email, :user_id) }
+ it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
+ it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
+ it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
+ it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
+ it { expect(event_data(key, :create)).to include(:username, :key, :id) }
+ it { expect(event_data(key, :destroy)).to include(:username, :key, :id) }
+
+ it do
+ expect(event_data(group, :create)).to include(
+ :event_name, :name, :created_at, :path, :group_id, :owner_name,
+ :owner_email
+ )
+ end
+ it do
+ expect(event_data(group, :destroy)).to include(
+ :event_name, :name, :created_at, :path, :group_id, :owner_name,
+ :owner_email
+ )
+ end
+ it do
+ expect(event_data(group_member, :create)).to include(
+ :event_name, :created_at, :group_name, :group_path, :group_id, :user_id,
+ :user_name, :user_email, :group_access
+ )
+ end
+ it do
+ expect(event_data(group_member, :destroy)).to include(
+ :event_name, :created_at, :group_name, :group_path, :group_id, :user_id,
+ :user_name, :user_email, :group_access
+ )
+ end
end
context 'event names' do
- it { event_name(user, :create).should eq "user_create" }
- it { event_name(user, :destroy).should eq "user_destroy" }
- it { event_name(project, :create).should eq "project_create" }
- it { event_name(project, :destroy).should eq "project_destroy" }
- it { event_name(project_member, :create).should eq "user_add_to_team" }
- it { event_name(project_member, :destroy).should eq "user_remove_from_team" }
- it { event_name(key, :create).should eq 'key_create' }
- it { event_name(key, :destroy).should eq 'key_destroy' }
+ it { expect(event_name(user, :create)).to eq "user_create" }
+ it { expect(event_name(user, :destroy)).to eq "user_destroy" }
+ it { expect(event_name(project, :create)).to eq "project_create" }
+ it { expect(event_name(project, :destroy)).to eq "project_destroy" }
+ it { expect(event_name(project_member, :create)).to eq "user_add_to_team" }
+ it { expect(event_name(project_member, :destroy)).to eq "user_remove_from_team" }
+ it { expect(event_name(key, :create)).to eq 'key_create' }
+ it { expect(event_name(key, :destroy)).to eq 'key_destroy' }
+ it { expect(event_name(group, :create)).to eq 'group_create' }
+ it { expect(event_name(group, :destroy)).to eq 'group_destroy' }
+ it { expect(event_name(group_member, :create)).to eq 'user_add_to_group' }
+ it { expect(event_name(group_member, :destroy)).to eq 'user_remove_from_group' }
end
def event_data(*args)
diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb
index 76af5bf7b88c9071e38bcad0f49d26b6bcdb40ec..d2b505f55a250bbc4ab2a9440abde3d8f060a8a2 100644
--- a/spec/services/test_hook_service_spec.rb
+++ b/spec/services/test_hook_service_spec.rb
@@ -8,7 +8,7 @@ describe TestHookService do
describe :execute do
it "should execute successfully" do
stub_request(:post, hook.url).to_return(status: 200)
- TestHookService.new.execute(hook, user).should be_true
+ expect(TestHookService.new.execute(hook, user)).to be_truthy
end
end
end
diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..841ef9bfed11e5e037b686e80a5f0ecfbae2e97a
--- /dev/null
+++ b/spec/services/update_snippet_service_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe UpdateSnippetService do
+ before do
+ @user = create :user
+ @admin = create :user, admin: true
+ @opts = {
+ title: 'Test snippet',
+ file_name: 'snippet.rb',
+ content: 'puts "hello world"',
+ visibility_level: Gitlab::VisibilityLevel::PRIVATE
+ }
+ end
+
+ context 'When public visibility is restricted' do
+ before do
+ allow_any_instance_of(ApplicationSetting).to(
+ receive(:restricted_visibility_levels).and_return(
+ [Gitlab::VisibilityLevel::PUBLIC]
+ )
+ )
+
+ @snippet = create_snippet(@project, @user, @opts)
+ @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'non-admins should not be able to update to public visibility' do
+ old_visibility = @snippet.visibility_level
+ update_snippet(@project, @user, @snippet, @opts)
+ expect(@snippet.errors.messages).to have_key(:visibility_level)
+ expect(@snippet.errors.messages[:visibility_level].first).to(
+ match('Public visibility has been restricted')
+ )
+ expect(@snippet.visibility_level).to eq(old_visibility)
+ end
+
+ it 'admins should be able to update to pubic visibility' do
+ old_visibility = @snippet.visibility_level
+ update_snippet(@project, @admin, @snippet, @opts)
+ expect(@snippet.visibility_level).not_to eq(old_visibility)
+ expect(@snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+ end
+
+ def create_snippet(project, user, opts)
+ CreateSnippetService.new(project, user, opts).execute
+ end
+
+ def update_snippet(project = nil, user, snippet, opts)
+ UpdateSnippetService.new(project, user, snippet, opts).execute
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 773de6628b17ce666760f08fba86983dceec98a0..53ccaa4fd6791833f2adcbaf028d579031bc2a39 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -37,8 +37,12 @@ RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.include TestEnv
+ config.infer_spec_type_from_file_location!
+ config.raise_errors_for_deprecations!
config.before(:suite) do
TestEnv.init
end
end
+
+ActiveRecord::Migration.maintain_test_schema!
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index d2d532d9738ad13def8132d23b0ec37497ad7253..cca7652093a6e17bb5013123c36a81d797231ecd 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -36,4 +36,15 @@ RSpec.configure do |config|
config.after(:each) do
DatabaseCleaner.clean
end
+
+ # rspec-rails 3 will no longer automatically infer an example group's spec type
+ # from the file location. You can explicitly opt-in to the feature using this
+ # config option.
+ # To explicitly tag specs without using automatic inference, set the `:type`
+ # metadata manually:
+ #
+ # describe ThingsController, :type => :controller do
+ # # Equivalent to being in spec/controllers
+ # end
+ config.infer_spec_type_from_file_location!
end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index ebd742066998d6548a9d6e61e160c4b75c7ce611..305592fa5a656fcaa937cea525fee7e4c0fad05d 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -39,7 +39,7 @@ def common_mentionable_setup
# unrecognized commits.
commitmap = { '1234567890a' => mentioned_commit }
extra_commits.each { |c| commitmap[c.short_id] = c }
- mproject.repository.stub(:commit) { |sha| commitmap[sha] }
+ allow(mproject.repository).to receive(:commit) { |sha| commitmap[sha] }
set_mentionable_text.call(ref_string)
end
end
@@ -48,19 +48,19 @@ shared_examples 'a mentionable' do
common_mentionable_setup
it 'generates a descriptive back-reference' do
- subject.gfm_reference.should == backref_text
+ expect(subject.gfm_reference).to eq(backref_text)
end
it "extracts references from its reference property" do
# De-duplicate and omit itself
refs = subject.references(mproject)
- refs.should have(6).items
- refs.should include(mentioned_issue)
- refs.should include(mentioned_mr)
- refs.should include(mentioned_commit)
- refs.should include(ext_issue)
- refs.should include(ext_mr)
- refs.should include(ext_commit)
+ expect(refs.size).to eq(6)
+ expect(refs).to include(mentioned_issue)
+ expect(refs).to include(mentioned_mr)
+ expect(refs).to include(mentioned_commit)
+ expect(refs).to include(ext_issue)
+ expect(refs).to include(ext_mr)
+ expect(refs).to include(ext_commit)
end
it 'creates cross-reference notes' do
@@ -68,7 +68,7 @@ shared_examples 'a mentionable' do
ext_issue, ext_mr, ext_commit]
mentioned_objects.each do |referenced|
- Note.should_receive(:create_cross_reference_note).with(referenced, subject.local_reference, mauthor, mproject)
+ expect(Note).to receive(:create_cross_reference_note).with(referenced, subject.local_reference, mauthor, mproject)
end
subject.create_cross_references!(mproject, mauthor)
@@ -77,8 +77,8 @@ shared_examples 'a mentionable' do
it 'detects existing cross-references' do
Note.create_cross_reference_note(mentioned_issue, subject.local_reference, mauthor, mproject)
- subject.has_mentioned?(mentioned_issue).should be_true
- subject.has_mentioned?(mentioned_mr).should be_false
+ expect(subject.has_mentioned?(mentioned_issue)).to be_truthy
+ expect(subject.has_mentioned?(mentioned_mr)).to be_falsey
end
end
@@ -95,12 +95,12 @@ shared_examples 'an editable mentionable' do
"#{ext_proj.path_with_namespace}##{other_ext_issue.iid}"
[mentioned_issue, mentioned_commit, ext_issue].each do |oldref|
- Note.should_not_receive(:create_cross_reference_note).with(oldref, subject.local_reference,
+ expect(Note).not_to receive(:create_cross_reference_note).with(oldref, subject.local_reference,
mauthor, mproject)
end
[other_issue, other_ext_issue].each do |newref|
- Note.should_receive(:create_cross_reference_note).with(
+ expect(Note).to receive(:create_cross_reference_note).with(
newref,
subject.local_reference,
mauthor,
diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb
index 4c4775da69237f54e4e689e500199769cb565d50..aadf791bf3ff1ee5c245dc11e31dd1ac1d6f8d25 100644
--- a/spec/support/repo_helpers.rb
+++ b/spec/support/repo_helpers.rb
@@ -43,6 +43,25 @@ eos
)
end
+ def another_sample_commit
+ OpenStruct.new(
+ id: "e56497bb5f03a90a51293fc6d516788730953899",
+ parent_id: '4cd80ccab63c82b4bad16faa5193fbd2aa06df40',
+ author_full_name: "Sytse Sijbrandij",
+ author_email: "sytse@gitlab.com",
+ files_changed_count: 1,
+ message: < 'e56497b',
'feature' => '0b4bc9a',
'feature_conflict' => 'bb5206f',
'fix' => '12d65c8',
@@ -18,21 +19,10 @@ module TestEnv
# See gitlab.yml.example test section for paths
#
def init(opts = {})
- RSpec::Mocks::setup(self)
-
# Disable mailer for spinach tests
disable_mailer if opts[:mailer] == false
- # Clean /tmp/tests
- tmp_test_path = Rails.root.join('tmp', 'tests')
-
- if File.directory?(tmp_test_path)
- Dir.entries(tmp_test_path).each do |entry|
- unless ['.', '..', 'gitlab-shell', factory_repo_name].include?(entry)
- FileUtils.rm_r(File.join(tmp_test_path, entry))
- end
- end
- end
+ clean_test_path
FileUtils.mkdir_p(repos_path)
@@ -48,18 +38,33 @@ module TestEnv
end
def enable_mailer
- NotificationService.any_instance.unstub(:mailer)
+ allow_any_instance_of(NotificationService).to receive(:mailer).and_call_original
+ end
+
+ # Clean /tmp/tests
+ #
+ # Keeps gitlab-shell and gitlab-test
+ def clean_test_path
+ tmp_test_path = Rails.root.join('tmp', 'tests', '**')
+
+ Dir[tmp_test_path].each do |entry|
+ unless File.basename(entry) =~ /\Agitlab-(shell|test)\z/
+ FileUtils.rm_rf(entry)
+ end
+ end
end
def setup_gitlab_shell
- `rake gitlab:shell:install`
+ unless File.directory?(Rails.root.join(*%w(tmp tests gitlab-shell)))
+ `rake gitlab:shell:install`
+ end
end
def setup_factory_repo
clone_url = "https://gitlab.com/gitlab-org/#{factory_repo_name}.git"
unless File.directory?(factory_repo_path)
- system(*%W(git clone #{clone_url} #{factory_repo_path}))
+ system(*%W(git clone -q #{clone_url} #{factory_repo_path}))
end
Dir.chdir(factory_repo_path) do
@@ -80,7 +85,7 @@ module TestEnv
end
# We must copy bare repositories because we will push to them.
- system(*%W(git clone --bare #{factory_repo_path} #{factory_repo_path_bare}))
+ system(git_env, *%W(git clone -q --bare #{factory_repo_path} #{factory_repo_path_bare}))
end
def copy_repo(project)
@@ -102,10 +107,16 @@ module TestEnv
end
def factory_repo_path_bare
- factory_repo_path.to_s + '_bare'
+ "#{factory_repo_path}_bare"
end
def factory_repo_name
'gitlab-test'
end
+
+ # Prevent developer git configurations from being persisted to test
+ # repositories
+ def git_env
+ {'GIT_TEMPLATE_DIR' => ''}
+ end
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 71a45eb2fa6f4e1ea03ea20814a41e71c5ab18da..8a411b7720ac4a2ad3ca7b3db583f1d111c0259e 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -10,21 +10,21 @@ describe 'gitlab:app namespace rake task' do
Rake::Task.define_task :environment
end
+ def run_rake_task(task_name)
+ Rake::Task[task_name].reenable
+ Rake.application.invoke_task task_name
+ end
+
describe 'backup_restore' do
before do
# avoid writing task output to spec progress
- $stdout.stub :write
- end
-
- let :run_rake_task do
- Rake::Task["gitlab:backup:restore"].reenable
- Rake.application.invoke_task "gitlab:backup:restore"
+ allow($stdout).to receive :write
end
context 'gitlab version' do
before do
Dir.stub glob: []
- Dir.stub :chdir
+ allow(Dir).to receive :chdir
File.stub exists?: true
Kernel.stub system: true
FileUtils.stub cp_r: true
@@ -36,17 +36,66 @@ describe 'gitlab:app namespace rake task' do
it 'should fail on mismatch' do
YAML.stub load_file: {gitlab_version: "not #{gitlab_version}" }
- expect { run_rake_task }.to raise_error SystemExit
+ expect { run_rake_task('gitlab:backup:restore') }.to(
+ raise_error SystemExit
+ )
end
it 'should invoke restoration on mach' do
YAML.stub load_file: {gitlab_version: gitlab_version}
- Rake::Task["gitlab:backup:db:restore"].should_receive :invoke
- Rake::Task["gitlab:backup:repo:restore"].should_receive :invoke
- Rake::Task["gitlab:shell:setup"].should_receive :invoke
- expect { run_rake_task }.to_not raise_error
+ expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke
+ expect(Rake::Task["gitlab:backup:repo:restore"]).to receive :invoke
+ expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke
+ expect { run_rake_task('gitlab:backup:restore') }.to_not raise_error
end
end
end # backup_restore task
+
+ describe 'backup_create' do
+ def tars_glob
+ Dir.glob(File.join(Gitlab.config.backup.path, '*_gitlab_backup.tar'))
+ end
+
+ before :all do
+ # Record the existing backup tars so we don't touch them
+ existing_tars = tars_glob
+
+ # Redirect STDOUT and run the rake task
+ orig_stdout = $stdout
+ $stdout = StringIO.new
+ run_rake_task('gitlab:backup:create')
+ $stdout = orig_stdout
+
+ @backup_tar = (tars_glob - existing_tars).first
+ end
+
+ after :all do
+ FileUtils.rm(@backup_tar)
+ end
+
+ it 'should set correct permissions on the tar file' do
+ expect(File.exist?(@backup_tar)).to be_truthy
+ expect(File::Stat.new(@backup_tar).mode.to_s(8)).to eq('100600')
+ end
+
+ it 'should set correct permissions on the tar contents' do
+ tar_contents, exit_status = Gitlab::Popen.popen(
+ %W{tar -tvf #{@backup_tar} db uploads repositories}
+ )
+ expect(exit_status).to eq(0)
+ expect(tar_contents).to match('db/')
+ expect(tar_contents).to match('uploads/')
+ expect(tar_contents).to match('repositories/')
+ expect(tar_contents).not_to match(/^.{4,9}[rwx]/)
+ end
+
+ it 'should delete temp directories' do
+ temp_dirs = Dir.glob(
+ File.join(Gitlab.config.backup.path, '{db,repositories,uploads}')
+ )
+
+ expect(temp_dirs).to be_empty
+ end
+ end # backup_create task
end # gitlab:app namespace
diff --git a/spec/tasks/gitlab/mail_google_schema_whitelisting.rb b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
new file mode 100644
index 0000000000000000000000000000000000000000..22e746870dc701cee6518b165970ff49df06a442
--- /dev/null
+++ b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'rake'
+
+describe 'gitlab:mail_google_schema_whitelisting rake task' do
+ before :all do
+ Rake.application.rake_require "tasks/gitlab/task_helpers"
+ Rake.application.rake_require "tasks/gitlab/mail_google_schema_whitelisting"
+ # empty task as env is already loaded
+ Rake::Task.define_task :environment
+ end
+
+ describe 'call' do
+ before do
+ # avoid writing task output to spec progress
+ allow($stdout).to receive :write
+ end
+
+ let :run_rake_task do
+ Rake::Task["gitlab:mail_google_schema_whitelisting"].reenable
+ Rake.application.invoke_task "gitlab:mail_google_schema_whitelisting"
+ end
+
+ it 'should run the task without errors' do
+ expect { run_rake_task }.to_not raise_error
+ end
+ end
+end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 4273fd1019a50e3d79ee17e1badb586bf084f12c..df1a2b84a53c644d443e40f1d536e4f45ba31fc2 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -1,9 +1,13 @@
require 'spec_helper'
describe PostReceive do
+ let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" }
+ let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") }
+ let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
+
context "as a resque worker" do
it "reponds to #perform" do
- PostReceive.new.should respond_to(:perform)
+ expect(PostReceive.new).to respond_to(:perform)
end
end
@@ -13,33 +17,29 @@ describe PostReceive do
let(:key_id) { key.shell_id }
it "fetches the correct project" do
- Project.should_receive(:find_with_namespace).with(project.path_with_namespace).and_return(project)
- PostReceive.new.perform(pwd(project), key_id, changes)
+ expect(Project).to receive(:find_with_namespace).with(project.path_with_namespace).and_return(project)
+ PostReceive.new.perform(pwd(project), key_id, base64_changes)
end
it "does not run if the author is not in the project" do
- Key.stub(:find_by).with(hash_including(id: anything())) { nil }
+ allow(Key).to receive(:find_by).with(hash_including(id: anything())) { nil }
- project.should_not_receive(:execute_hooks)
+ expect(project).not_to receive(:execute_hooks)
- PostReceive.new.perform(pwd(project), key_id, changes).should be_false
+ expect(PostReceive.new.perform(pwd(project), key_id, base64_changes)).to be_falsey
end
it "asks the project to trigger all hooks" do
Project.stub(find_with_namespace: project)
- project.should_receive(:execute_hooks)
- project.should_receive(:execute_services)
- project.should_receive(:update_merge_requests)
+ expect(project).to receive(:execute_hooks).twice
+ expect(project).to receive(:execute_services).twice
+ expect(project).to receive(:update_merge_requests)
- PostReceive.new.perform(pwd(project), key_id, changes)
+ PostReceive.new.perform(pwd(project), key_id, base64_changes)
end
end
def pwd(project)
File.join(Gitlab.config.gitlab_shell.repos_path, project.path_with_namespace)
end
-
- def changes
- 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master'
- end
end
diff --git a/vendor/assets/javascripts/chart-lib.min.js b/vendor/assets/javascripts/chart-lib.min.js
old mode 100755
new mode 100644
diff --git a/vendor/assets/javascripts/highlight.pack.js b/vendor/assets/javascripts/highlight.pack.js
deleted file mode 100644
index c09eac02df97a02e6be95be02ae06c378577b6ba..0000000000000000000000000000000000000000
--- a/vendor/assets/javascripts/highlight.pack.js
+++ /dev/null
@@ -1 +0,0 @@
-var hljs=new function(){function j(v){return v.replace(/&/gm,"&").replace(//gm,">")}function t(v){return v.nodeName.toLowerCase()}function h(w,x){var v=w&&w.exec(x);return v&&v.index==0}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^lang(uage)?-/,"")});return v.filter(function(x){return i(x)||/no(-?)highlight/.test(x)})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);if(!t(A).match(/br|hr|img|input/)){v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset"}function E(G){F+=""+t(G)+">"}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=j(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+j(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};var E=function(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})};if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b="\\b("+D.bK.split(" ").join("|")+")\\b"}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?("+F.b+")\\.?":F.b}).concat([D.tE,D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}}}x(y)}function c(T,L,J,R){function v(V,W){for(var U=0;U";V+=aa+'">';return V+Y+Z}function N(){if(!I.k){return j(C)}var U="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(C);while(V){U+=j(C.substr(X,V.index-X));var W=E(I,V);if(W){H+=W[1];U+=w(W[0],j(V[0]))}else{U+=j(V[0])}X=I.lR.lastIndex;V=I.lR.exec(C)}return U+j(C.substr(X))}function F(){if(I.sL&&!f[I.sL]){return j(C)}var U=I.sL?c(I.sL,C,true,S):e(C);if(I.r>0){H+=U.r}if(I.subLanguageMode=="continuous"){S=U.top}return w(U.language,U.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(W,V){var U=W.cN?w(W.cN,"",true):"";if(W.rB){D+=U;C=""}else{if(W.eB){D+=j(V)+U;C=""}else{D+=U;C=V}}I=Object.create(W,{parent:{value:I}})}function G(U,Y){C+=U;if(Y===undefined){D+=Q();return 0}var W=v(Y,I);if(W){D+=Q();P(W,Y);return W.rB?0:Y.length}var X=z(I,Y);if(X){var V=I;if(!(V.rE||V.eE)){C+=Y}D+=Q();do{if(I.cN){D+=""}H+=I.r;I=I.parent}while(I!=X.parent);if(V.eE){D+=j(Y)}C="";if(X.starts){P(X.starts,"")}return V.rE?0:Y.length}if(A(Y,I)){throw new Error('Illegal lexeme "'+Y+'" for mode "'+(I.cN||"")+'"')}C+=Y;return Y.length||1}var M=i(T);if(!M){throw new Error('Unknown language: "'+T+'"')}m(M);var I=R||M;var S;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,"",true)+D}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+=""}}return{r:H,value:D,language:T,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:j(L)}}else{throw O}}}function e(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:j(y)};var w=v;x.forEach(function(z){if(!i(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function g(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"
")}return v}function p(A){var B=r(A);if(/no(-?)highlight/.test(B)){return}var y;if(b.useBR){y=document.createElementNS("http://www.w3.org/1999/xhtml","div");y.innerHTML=A.innerHTML.replace(/\n/g,"").replace(/
/g,"\n")}else{y=A}var z=y.textContent;var v=B?c(B,z,true):e(z);var x=u(y);if(x.length){var w=document.createElementNS("http://www.w3.org/1999/xhtml","div");w.innerHTML=v.value;v.value=q(x,u(w),z)}v.value=g(v.value);A.innerHTML=v.value;A.className+=" hljs "+(!B&&v.language||"");A.result={language:v.language,re:v.r};if(v.second_best){A.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function d(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function k(){return Object.keys(f)}function i(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=e;this.fixMarkup=g;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=d;this.listLanguages=k;this.getLanguage=i;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/};this.CLCM={cN:"comment",b:"//",e:"$",c:[this.PWM]};this.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[this.PWM]};this.HCM={cN:"comment",b:"#",e:"$",c:[this.PWM]};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.CSSNM={cN:"number",b:this.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0};this.RM={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("1c",function(b){var f="[a-zA-Zа-яА-Я][a-zA-Z0-9_а-яА-Я]*";var c="возврат дата для если и или иначе иначеесли исключение конецесли конецпопытки конецпроцедуры конецфункции конеццикла константа не перейти перем перечисление по пока попытка прервать продолжить процедура строка тогда фс функция цикл число экспорт";var e="ansitooem oemtoansi ввестивидсубконто ввестидату ввестизначение ввестиперечисление ввестипериод ввестиплансчетов ввестистроку ввестичисло вопрос восстановитьзначение врег выбранныйплансчетов вызватьисключение датагод датамесяц датачисло добавитьмесяц завершитьработусистемы заголовоксистемы записьжурналарегистрации запуститьприложение зафиксироватьтранзакцию значениевстроку значениевстрокувнутр значениевфайл значениеизстроки значениеизстрокивнутр значениеизфайла имякомпьютера имяпользователя каталогвременныхфайлов каталогиб каталогпользователя каталогпрограммы кодсимв командасистемы конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лев лог лог10 макс максимальноеколичествосубконто мин монопольныйрежим названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найти найтипомеченныенаудаление найтиссылки началопериодаби началостандартногоинтервала начатьтранзакцию начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода нрег обработкаожидания окр описаниеошибки основнойжурналрасчетов основнойплансчетов основнойязык открытьформу открытьформумодально отменитьтранзакцию очиститьокносообщений периодстр полноеимяпользователя получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта прав праводоступа предупреждение префиксавтонумерации пустаястрока пустоезначение рабочаядаттьпустоезначение рабочаядата разделительстраниц разделительстрок разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо сигнал симв символтабуляции создатьобъект сокрл сокрлп сокрп сообщить состояние сохранитьзначение сред статусвозврата стрдлина стрзаменить стрколичествострок стрполучитьстроку стрчисловхождений сформироватьпозициюдокумента счетпокоду текущаядата текущеевремя типзначения типзначениястр удалитьобъекты установитьтана установитьтапо фиксшаблон формат цел шаблон";var a={cN:"dquote",b:'""'};var d={cN:"string",b:'"',e:'"|$',c:[a]};var g={cN:"string",b:"\\|",e:'"|$',c:[a]};return{cI:true,l:f,k:{keyword:c,built_in:e},c:[b.CLCM,b.NM,d,g,{cN:"function",b:"(процедура|функция)",e:"$",l:f,k:"процедура функция",c:[b.inherit(b.TM,{b:f}),{cN:"tail",eW:true,c:[{cN:"params",b:"\\(",e:"\\)",l:f,k:"знач",c:[d,g]},{cN:"export",b:"экспорт",eW:true,l:f,k:"экспорт",c:[b.CLCM]}]},b.CLCM]},{cN:"preprocessor",b:"#",e:"$"},{cN:"date",b:"'\\d{2}\\.\\d{2}\\.(\\d{2}|\\d{4})'"}]}});hljs.registerLanguage("actionscript",function(a){var c="[a-zA-Z_$][a-zA-Z0-9_$]*";var b="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)";var d={cN:"rest_arg",b:"[.]{3}",e:c,r:10};return{aliases:["as"],k:{keyword:"as break case catch class const continue default delete do dynamic each else extends final finally for function get if implements import in include instanceof interface internal is namespace native new override package private protected public return set static super switch this throw try typeof use var void while with",literal:"true false null undefined"},c:[a.ASM,a.QSM,a.CLCM,a.CBCM,a.CNM,{cN:"package",bK:"package",e:"{",c:[a.TM]},{cN:"class",bK:"class interface",e:"{",eE:true,c:[{bK:"extends implements"},a.TM]},{cN:"preprocessor",bK:"import include",e:";"},{cN:"function",bK:"function",e:"[{;]",eE:true,i:"\\S",c:[a.TM,{cN:"params",b:"\\(",e:"\\)",c:[a.ASM,a.QSM,a.CLCM,a.CBCM,d]},{cN:"type",b:":",e:b,r:10}]}]}});hljs.registerLanguage("apache",function(a){var b={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:true,c:[a.HCM,{cN:"tag",b:"?",e:">"},{cN:"keyword",b:/\w+/,r:0,k:{common:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"sqbracket",b:"\\s\\[",e:"\\]$"},{cN:"cbracket",b:"[\\$%]\\{",e:"\\}",c:["self",b]},b,a.QSM]}}],i:/\S/}});hljs.registerLanguage("applescript",function(a){var b=a.inherit(a.QSM,{i:""});var d={cN:"params",b:"\\(",e:"\\)",c:["self",a.CNM,b]};var c=[{cN:"comment",b:"--",e:"$"},{cN:"comment",b:"\\(\\*",e:"\\*\\)",c:["self",{b:"--",e:"$"}]},a.HCM];return{aliases:["osascript"],k:{keyword:"about above after against and around as at back before beginning behind below beneath beside between but by considering contain contains continue copy div does eighth else end equal equals error every exit fifth first for fourth from front get given global if ignoring in into is it its last local me middle mod my ninth not of on onto or over prop property put ref reference repeat returning script second set seventh since sixth some tell tenth that the|0 then third through thru timeout times to transaction try until where while whose with without",constant:"AppleScript false linefeed return pi quote result space tab true",type:"alias application boolean class constant date file integer list number real record string text",command:"activate beep count delay launch log offset read round run say summarize write",property:"character characters contents day frontmost id item length month name paragraph paragraphs rest reverse running time version weekday word words year"},c:[b,a.CNM,{cN:"type",b:"\\bPOSIX file\\b"},{cN:"command",b:"\\b(clipboard info|the clipboard|info for|list (disks|folder)|mount volume|path to|(close|open for) access|(get|set) eof|current date|do shell script|get volume settings|random number|set volume|system attribute|system info|time to GMT|(load|run|store) script|scripting components|ASCII (character|number)|localized string|choose (application|color|file|file name|folder|from list|remote application|URL)|display (alert|dialog))\\b|^\\s*return\\b"},{cN:"constant",b:"\\b(text item delimiters|current application|missing value)\\b"},{cN:"keyword",b:"\\b(apart from|aside from|instead of|out of|greater than|isn't|(doesn't|does not) (equal|come before|come after|contain)|(greater|less) than( or equal)?|(starts?|ends|begins?) with|contained by|comes (before|after)|a (ref|reference))\\b"},{cN:"property",b:"\\b(POSIX path|(date|time) string|quoted form)\\b"},{cN:"function_start",bK:"on",i:"[${=;\\n]",c:[a.UTM,d]}].concat(c),i:"//"}});hljs.registerLanguage("xml",function(a){var c="[A-Za-z0-9\\._:-]+";var d={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"};var b={eW:true,i:/,r:0,c:[d,{cN:"attribute",b:c,r:0},{b:"=",r:0,c:[{cN:"value",v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:true,c:[{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"