Revision 1298:4f746d8966dd .svn/pristine/29
| .svn/pristine/29/2916f748eef73ff691bcfc74e7c1ac23960d090b.svn-base | ||
|---|---|---|
| 1 |
# $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $ |
|
| 2 |
# |
|
| 3 |
# NET::BER |
|
| 4 |
# Mixes ASN.1/BER convenience methods into several standard classes. |
|
| 5 |
# Also provides BER parsing functionality. |
|
| 6 |
# |
|
| 7 |
#---------------------------------------------------------------------------- |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved. |
|
| 10 |
# |
|
| 11 |
# Gmail: garbagecat10 |
|
| 12 |
# |
|
| 13 |
# This program is free software; you can redistribute it and/or modify |
|
| 14 |
# it under the terms of the GNU General Public License as published by |
|
| 15 |
# the Free Software Foundation; either version 2 of the License, or |
|
| 16 |
# (at your option) any later version. |
|
| 17 |
# |
|
| 18 |
# This program is distributed in the hope that it will be useful, |
|
| 19 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 20 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 21 |
# GNU General Public License for more details. |
|
| 22 |
# |
|
| 23 |
# You should have received a copy of the GNU General Public License |
|
| 24 |
# along with this program; if not, write to the Free Software |
|
| 25 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
| 26 |
# |
|
| 27 |
#--------------------------------------------------------------------------- |
|
| 28 |
# |
|
| 29 |
# |
|
| 30 |
|
|
| 31 |
|
|
| 32 |
|
|
| 33 |
|
|
| 34 |
module Net |
|
| 35 |
|
|
| 36 |
module BER |
|
| 37 |
|
|
| 38 |
class BerError < Exception; end |
|
| 39 |
|
|
| 40 |
|
|
| 41 |
# This module is for mixing into IO and IO-like objects. |
|
| 42 |
module BERParser |
|
| 43 |
|
|
| 44 |
# The order of these follows the class-codes in BER. |
|
| 45 |
# Maybe this should have been a hash. |
|
| 46 |
TagClasses = [:universal, :application, :context_specific, :private] |
|
| 47 |
|
|
| 48 |
BuiltinSyntax = {
|
|
| 49 |
:universal => {
|
|
| 50 |
:primitive => {
|
|
| 51 |
1 => :boolean, |
|
| 52 |
2 => :integer, |
|
| 53 |
4 => :string, |
|
| 54 |
10 => :integer, |
|
| 55 |
}, |
|
| 56 |
:constructed => {
|
|
| 57 |
16 => :array, |
|
| 58 |
17 => :array |
|
| 59 |
} |
|
| 60 |
} |
|
| 61 |
} |
|
| 62 |
|
|
| 63 |
# |
|
| 64 |
# read_ber |
|
| 65 |
# TODO: clean this up so it works properly with partial |
|
| 66 |
# packets coming from streams that don't block when |
|
| 67 |
# we ask for more data (like StringIOs). At it is, |
|
| 68 |
# this can throw TypeErrors and other nasties. |
|
| 69 |
# |
|
| 70 |
def read_ber syntax=nil |
|
| 71 |
return nil if (StringIO == self.class) and eof? |
|
| 72 |
|
|
| 73 |
id = getc # don't trash this value, we'll use it later |
|
| 74 |
tag = id & 31 |
|
| 75 |
tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
|
|
| 76 |
tagclass = TagClasses[ id >> 6 ] |
|
| 77 |
encoding = (id & 0x20 != 0) ? :constructed : :primitive |
|
| 78 |
|
|
| 79 |
n = getc |
|
| 80 |
lengthlength,contentlength = if n <= 127 |
|
| 81 |
[1,n] |
|
| 82 |
else |
|
| 83 |
j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
|
|
| 84 |
[1 + (n & 127), j] |
|
| 85 |
end |
|
| 86 |
|
|
| 87 |
newobj = read contentlength |
|
| 88 |
|
|
| 89 |
objtype = nil |
|
| 90 |
[syntax, BuiltinSyntax].each {|syn|
|
|
| 91 |
if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag] |
|
| 92 |
objtype = ot[tag] |
|
| 93 |
break |
|
| 94 |
end |
|
| 95 |
} |
|
| 96 |
|
|
| 97 |
obj = case objtype |
|
| 98 |
when :boolean |
|
| 99 |
newobj != "\000" |
|
| 100 |
when :string |
|
| 101 |
(newobj || "").dup |
|
| 102 |
when :integer |
|
| 103 |
j = 0 |
|
| 104 |
newobj.each_byte {|b| j = (j << 8) + b}
|
|
| 105 |
j |
|
| 106 |
when :array |
|
| 107 |
seq = [] |
|
| 108 |
sio = StringIO.new( newobj || "" ) |
|
| 109 |
# Interpret the subobject, but note how the loop |
|
| 110 |
# is built: nil ends the loop, but false (a valid |
|
| 111 |
# BER value) does not! |
|
| 112 |
while (e = sio.read_ber(syntax)) != nil |
|
| 113 |
seq << e |
|
| 114 |
end |
|
| 115 |
seq |
|
| 116 |
else |
|
| 117 |
raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
|
|
| 118 |
end |
|
| 119 |
|
|
| 120 |
# Add the identifier bits into the object if it's a String or an Array. |
|
| 121 |
# We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway. |
|
| 122 |
obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
|
|
| 123 |
obj |
|
| 124 |
|
|
| 125 |
end |
|
| 126 |
|
|
| 127 |
end # module BERParser |
|
| 128 |
end # module BER |
|
| 129 |
|
|
| 130 |
end # module Net |
|
| 131 |
|
|
| 132 |
|
|
| 133 |
class IO |
|
| 134 |
include Net::BER::BERParser |
|
| 135 |
end |
|
| 136 |
|
|
| 137 |
require "stringio" |
|
| 138 |
class StringIO |
|
| 139 |
include Net::BER::BERParser |
|
| 140 |
end |
|
| 141 |
|
|
| 142 |
begin |
|
| 143 |
require 'openssl' |
|
| 144 |
class OpenSSL::SSL::SSLSocket |
|
| 145 |
include Net::BER::BERParser |
|
| 146 |
end |
|
| 147 |
rescue LoadError |
|
| 148 |
# Ignore LoadError. |
|
| 149 |
# DON'T ignore NameError, which means the SSLSocket class |
|
| 150 |
# is somehow unavailable on this implementation of Ruby's openssl. |
|
| 151 |
# This may be WRONG, however, because we don't yet know how Ruby's |
|
| 152 |
# openssl behaves on machines with no OpenSSL library. I suppose |
|
| 153 |
# it's possible they do not fail to require 'openssl' but do not |
|
| 154 |
# create the classes. So this code is provisional. |
|
| 155 |
# Also, you might think that OpenSSL::SSL::SSLSocket inherits from |
|
| 156 |
# IO so we'd pick it up above. But you'd be wrong. |
|
| 157 |
end |
|
| 158 |
|
|
| 159 |
class String |
|
| 160 |
def read_ber syntax=nil |
|
| 161 |
StringIO.new(self).read_ber(syntax) |
|
| 162 |
end |
|
| 163 |
end |
|
| 164 |
|
|
| 165 |
|
|
| 166 |
|
|
| 167 |
#---------------------------------------------- |
|
| 168 |
|
|
| 169 |
|
|
| 170 |
class FalseClass |
|
| 171 |
# |
|
| 172 |
# to_ber |
|
| 173 |
# |
|
| 174 |
def to_ber |
|
| 175 |
"\001\001\000" |
|
| 176 |
end |
|
| 177 |
end |
|
| 178 |
|
|
| 179 |
|
|
| 180 |
class TrueClass |
|
| 181 |
# |
|
| 182 |
# to_ber |
|
| 183 |
# |
|
| 184 |
def to_ber |
|
| 185 |
"\001\001\001" |
|
| 186 |
end |
|
| 187 |
end |
|
| 188 |
|
|
| 189 |
|
|
| 190 |
|
|
| 191 |
class Fixnum |
|
| 192 |
# |
|
| 193 |
# to_ber |
|
| 194 |
# |
|
| 195 |
def to_ber |
|
| 196 |
i = [self].pack('w')
|
|
| 197 |
[2, i.length].pack("CC") + i
|
|
| 198 |
end |
|
| 199 |
|
|
| 200 |
# |
|
| 201 |
# to_ber_enumerated |
|
| 202 |
# |
|
| 203 |
def to_ber_enumerated |
|
| 204 |
i = [self].pack('w')
|
|
| 205 |
[10, i.length].pack("CC") + i
|
|
| 206 |
end |
|
| 207 |
|
|
| 208 |
# |
|
| 209 |
# to_ber_length_encoding |
|
| 210 |
# |
|
| 211 |
def to_ber_length_encoding |
|
| 212 |
if self <= 127 |
|
| 213 |
[self].pack('C')
|
|
| 214 |
else |
|
| 215 |
i = [self].pack('N').sub(/^[\0]+/,"")
|
|
| 216 |
[0x80 + i.length].pack('C') + i
|
|
| 217 |
end |
|
| 218 |
end |
|
| 219 |
|
|
| 220 |
end # class Fixnum |
|
| 221 |
|
|
| 222 |
|
|
| 223 |
class Bignum |
|
| 224 |
|
|
| 225 |
def to_ber |
|
| 226 |
i = [self].pack('w')
|
|
| 227 |
i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" ) |
|
| 228 |
[2, i.length].pack("CC") + i
|
|
| 229 |
end |
|
| 230 |
|
|
| 231 |
end |
|
| 232 |
|
|
| 233 |
|
|
| 234 |
|
|
| 235 |
class String |
|
| 236 |
# |
|
| 237 |
# to_ber |
|
| 238 |
# A universal octet-string is tag number 4, |
|
| 239 |
# but others are possible depending on the context, so we |
|
| 240 |
# let the caller give us one. |
|
| 241 |
# The preferred way to do this in user code is via to_ber_application_sring |
|
| 242 |
# and to_ber_contextspecific. |
|
| 243 |
# |
|
| 244 |
def to_ber code = 4 |
|
| 245 |
[code].pack('C') + length.to_ber_length_encoding + self
|
|
| 246 |
end |
|
| 247 |
|
|
| 248 |
# |
|
| 249 |
# to_ber_application_string |
|
| 250 |
# |
|
| 251 |
def to_ber_application_string code |
|
| 252 |
to_ber( 0x40 + code ) |
|
| 253 |
end |
|
| 254 |
|
|
| 255 |
# |
|
| 256 |
# to_ber_contextspecific |
|
| 257 |
# |
|
| 258 |
def to_ber_contextspecific code |
|
| 259 |
to_ber( 0x80 + code ) |
|
| 260 |
end |
|
| 261 |
|
|
| 262 |
end # class String |
|
| 263 |
|
|
| 264 |
|
|
| 265 |
|
|
| 266 |
class Array |
|
| 267 |
# |
|
| 268 |
# to_ber_appsequence |
|
| 269 |
# An application-specific sequence usually gets assigned |
|
| 270 |
# a tag that is meaningful to the particular protocol being used. |
|
| 271 |
# This is different from the universal sequence, which usually |
|
| 272 |
# gets a tag value of 16. |
|
| 273 |
# Now here's an interesting thing: We're adding the X.690 |
|
| 274 |
# "application constructed" code at the top of the tag byte (0x60), |
|
| 275 |
# but some clients, notably ldapsearch, send "context-specific |
|
| 276 |
# constructed" (0xA0). The latter would appear to violate RFC-1777, |
|
| 277 |
# but what do I know? We may need to change this. |
|
| 278 |
# |
|
| 279 |
|
|
| 280 |
def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end |
|
| 281 |
def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end |
|
| 282 |
def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end |
|
| 283 |
def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end |
|
| 284 |
def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end |
|
| 285 |
|
|
| 286 |
private |
|
| 287 |
def to_ber_seq_internal code |
|
| 288 |
s = self.to_s |
|
| 289 |
[code].pack('C') + s.length.to_ber_length_encoding + s
|
|
| 290 |
end |
|
| 291 |
|
|
| 292 |
end # class Array |
|
| 293 |
|
|
| 294 |
|
|
| .svn/pristine/29/299dc6b637e0f5b3775b92631d240971508c0684.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang |
|
| 3 |
# |
|
| 4 |
# This program is free software; you can redistribute it and/or |
|
| 5 |
# modify it under the terms of the GNU General Public License |
|
| 6 |
# as published by the Free Software Foundation; either version 2 |
|
| 7 |
# of the License, or (at your option) any later version. |
|
| 8 |
# |
|
| 9 |
# This program is distributed in the hope that it will be useful, |
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 12 |
# GNU General Public License for more details. |
|
| 13 |
# |
|
| 14 |
# You should have received a copy of the GNU General Public License |
|
| 15 |
# along with this program; if not, write to the Free Software |
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 17 |
|
|
| 18 |
require File.expand_path('../../../../test_helper', __FILE__)
|
|
| 19 |
|
|
| 20 |
class Redmine::MimeTypeTest < ActiveSupport::TestCase |
|
| 21 |
|
|
| 22 |
def test_of |
|
| 23 |
to_test = {'test.unk' => nil,
|
|
| 24 |
'test.txt' => 'text/plain', |
|
| 25 |
'test.c' => 'text/x-c', |
|
| 26 |
} |
|
| 27 |
to_test.each do |name, expected| |
|
| 28 |
assert_equal expected, Redmine::MimeType.of(name) |
|
| 29 |
end |
|
| 30 |
end |
|
| 31 |
|
|
| 32 |
def test_css_class_of |
|
| 33 |
to_test = {'test.unk' => nil,
|
|
| 34 |
'test.txt' => 'text-plain', |
|
| 35 |
'test.c' => 'text-x-c', |
|
| 36 |
} |
|
| 37 |
to_test.each do |name, expected| |
|
| 38 |
assert_equal expected, Redmine::MimeType.css_class_of(name) |
|
| 39 |
end |
|
| 40 |
end |
|
| 41 |
|
|
| 42 |
def test_main_mimetype_of |
|
| 43 |
to_test = {'test.unk' => nil,
|
|
| 44 |
'test.txt' => 'text', |
|
| 45 |
'test.c' => 'text', |
|
| 46 |
} |
|
| 47 |
to_test.each do |name, expected| |
|
| 48 |
assert_equal expected, Redmine::MimeType.main_mimetype_of(name) |
|
| 49 |
end |
|
| 50 |
end |
|
| 51 |
|
|
| 52 |
def test_is_type |
|
| 53 |
to_test = {['text', 'test.unk'] => false,
|
|
| 54 |
['text', 'test.txt'] => true, |
|
| 55 |
['text', 'test.c'] => true, |
|
| 56 |
} |
|
| 57 |
to_test.each do |args, expected| |
|
| 58 |
assert_equal expected, Redmine::MimeType.is_type?(*args) |
|
| 59 |
end |
|
| 60 |
end |
|
| 61 |
end |
|
| .svn/pristine/29/29d1b7a44a44f4040dd2b8d4fe4b9b40b3383592.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang |
|
| 3 |
# |
|
| 4 |
# This program is free software; you can redistribute it and/or |
|
| 5 |
# modify it under the terms of the GNU General Public License |
|
| 6 |
# as published by the Free Software Foundation; either version 2 |
|
| 7 |
# of the License, or (at your option) any later version. |
|
| 8 |
# |
|
| 9 |
# This program is distributed in the hope that it will be useful, |
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 12 |
# GNU General Public License for more details. |
|
| 13 |
# |
|
| 14 |
# You should have received a copy of the GNU General Public License |
|
| 15 |
# along with this program; if not, write to the Free Software |
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 17 |
|
|
| 18 |
class TrackersController < ApplicationController |
|
| 19 |
layout 'admin' |
|
| 20 |
|
|
| 21 |
before_filter :require_admin, :except => :index |
|
| 22 |
before_filter :require_admin_or_api_request, :only => :index |
|
| 23 |
accept_api_auth :index |
|
| 24 |
|
|
| 25 |
def index |
|
| 26 |
respond_to do |format| |
|
| 27 |
format.html {
|
|
| 28 |
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position' |
|
| 29 |
render :action => "index", :layout => false if request.xhr? |
|
| 30 |
} |
|
| 31 |
format.api {
|
|
| 32 |
@trackers = Tracker.all |
|
| 33 |
} |
|
| 34 |
end |
|
| 35 |
end |
|
| 36 |
|
|
| 37 |
def new |
|
| 38 |
@tracker ||= Tracker.new(params[:tracker]) |
|
| 39 |
@trackers = Tracker.find :all, :order => 'position' |
|
| 40 |
@projects = Project.find(:all) |
|
| 41 |
end |
|
| 42 |
|
|
| 43 |
def create |
|
| 44 |
@tracker = Tracker.new(params[:tracker]) |
|
| 45 |
if request.post? and @tracker.save |
|
| 46 |
# workflow copy |
|
| 47 |
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from])) |
|
| 48 |
@tracker.workflows.copy(copy_from) |
|
| 49 |
end |
|
| 50 |
flash[:notice] = l(:notice_successful_create) |
|
| 51 |
redirect_to :action => 'index' |
|
| 52 |
return |
|
| 53 |
end |
|
| 54 |
new |
|
| 55 |
render :action => 'new' |
|
| 56 |
end |
|
| 57 |
|
|
| 58 |
def edit |
|
| 59 |
@tracker ||= Tracker.find(params[:id]) |
|
| 60 |
@projects = Project.find(:all) |
|
| 61 |
end |
|
| 62 |
|
|
| 63 |
def update |
|
| 64 |
@tracker = Tracker.find(params[:id]) |
|
| 65 |
if request.put? and @tracker.update_attributes(params[:tracker]) |
|
| 66 |
flash[:notice] = l(:notice_successful_update) |
|
| 67 |
redirect_to :action => 'index' |
|
| 68 |
return |
|
| 69 |
end |
|
| 70 |
edit |
|
| 71 |
render :action => 'edit' |
|
| 72 |
end |
|
| 73 |
|
|
| 74 |
verify :method => :delete, :only => :destroy, :redirect_to => { :action => :index }
|
|
| 75 |
def destroy |
|
| 76 |
@tracker = Tracker.find(params[:id]) |
|
| 77 |
unless @tracker.issues.empty? |
|
| 78 |
flash[:error] = l(:error_can_not_delete_tracker) |
|
| 79 |
else |
|
| 80 |
@tracker.destroy |
|
| 81 |
end |
|
| 82 |
redirect_to :action => 'index' |
|
| 83 |
end |
|
| 84 |
end |
|
| .svn/pristine/29/29e480c531a7477e290d81b189c3105531d0d4db.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
| 3 |
# |
|
| 4 |
# This program is free software; you can redistribute it and/or |
|
| 5 |
# modify it under the terms of the GNU General Public License |
|
| 6 |
# as published by the Free Software Foundation; either version 2 |
|
| 7 |
# of the License, or (at your option) any later version. |
|
| 8 |
# |
|
| 9 |
# This program is distributed in the hope that it will be useful, |
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 12 |
# GNU General Public License for more details. |
|
| 13 |
# |
|
| 14 |
# You should have received a copy of the GNU General Public License |
|
| 15 |
# along with this program; if not, write to the Free Software |
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 17 |
|
|
| 18 |
require File.expand_path('../base', __FILE__)
|
|
| 19 |
|
|
| 20 |
class Redmine::UiTest::IssuesTest < Redmine::UiTest::Base |
|
| 21 |
fixtures :projects, :users, :roles, :members, :member_roles, |
|
| 22 |
:trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues, |
|
| 23 |
:enumerations, :custom_fields, :custom_values, :custom_fields_trackers, |
|
| 24 |
:watchers |
|
| 25 |
|
|
| 26 |
def test_create_issue |
|
| 27 |
log_user('jsmith', 'jsmith')
|
|
| 28 |
visit '/projects/ecookbook/issues/new' |
|
| 29 |
within('form#issue-form') do
|
|
| 30 |
select 'Bug', :from => 'Tracker' |
|
| 31 |
select 'Low', :from => 'Priority' |
|
| 32 |
fill_in 'Subject', :with => 'new test issue' |
|
| 33 |
fill_in 'Description', :with => 'new issue' |
|
| 34 |
select '0 %', :from => 'Done' |
|
| 35 |
fill_in 'Due date', :with => '' |
|
| 36 |
select '', :from => 'Assignee' |
|
| 37 |
fill_in 'Searchable field', :with => 'Value for field 2' |
|
| 38 |
# click_button 'Create' would match both 'Create' and 'Create and continue' buttons |
|
| 39 |
find('input[name=commit]').click
|
|
| 40 |
end |
|
| 41 |
|
|
| 42 |
# find created issue |
|
| 43 |
issue = Issue.find_by_subject("new test issue")
|
|
| 44 |
assert_kind_of Issue, issue |
|
| 45 |
|
|
| 46 |
# check redirection |
|
| 47 |
find 'div#flash_notice', :visible => true, :text => "Issue \##{issue.id} created."
|
|
| 48 |
assert_equal issue_path(:id => issue), current_path |
|
| 49 |
|
|
| 50 |
# check issue attributes |
|
| 51 |
assert_equal 'jsmith', issue.author.login |
|
| 52 |
assert_equal 1, issue.project.id |
|
| 53 |
assert_equal IssueStatus.find_by_name('New'), issue.status
|
|
| 54 |
assert_equal Tracker.find_by_name('Bug'), issue.tracker
|
|
| 55 |
assert_equal IssuePriority.find_by_name('Low'), issue.priority
|
|
| 56 |
assert_equal 'Value for field 2', issue.custom_field_value(CustomField.find_by_name('Searchable field'))
|
|
| 57 |
end |
|
| 58 |
|
|
| 59 |
def test_create_issue_with_form_update |
|
| 60 |
field1 = IssueCustomField.create!( |
|
| 61 |
:field_format => 'string', |
|
| 62 |
:name => 'Field1', |
|
| 63 |
:is_for_all => true, |
|
| 64 |
:trackers => Tracker.find_all_by_id([1, 2]) |
|
| 65 |
) |
|
| 66 |
field2 = IssueCustomField.create!( |
|
| 67 |
:field_format => 'string', |
|
| 68 |
:name => 'Field2', |
|
| 69 |
:is_for_all => true, |
|
| 70 |
:trackers => Tracker.find_all_by_id(2) |
|
| 71 |
) |
|
| 72 |
|
|
| 73 |
Role.non_member.add_permission! :add_issues |
|
| 74 |
Role.non_member.remove_permission! :edit_issues, :add_issue_notes |
|
| 75 |
|
|
| 76 |
log_user('someone', 'foo')
|
|
| 77 |
visit '/projects/ecookbook/issues/new' |
|
| 78 |
assert page.has_no_content?(field2.name) |
|
| 79 |
assert page.has_content?(field1.name) |
|
| 80 |
|
|
| 81 |
fill_in 'Subject', :with => 'New test issue' |
|
| 82 |
fill_in 'Description', :with => 'New test issue description' |
|
| 83 |
fill_in field1.name, :with => 'CF1 value' |
|
| 84 |
select 'Low', :from => 'Priority' |
|
| 85 |
|
|
| 86 |
# field2 should show up when changing tracker |
|
| 87 |
select 'Feature request', :from => 'Tracker' |
|
| 88 |
assert page.has_content?(field2.name) |
|
| 89 |
assert page.has_content?(field1.name) |
|
| 90 |
|
|
| 91 |
fill_in field2.name, :with => 'CF2 value' |
|
| 92 |
assert_difference 'Issue.count' do |
|
| 93 |
page.first(:button, 'Create').click |
|
| 94 |
end |
|
| 95 |
|
|
| 96 |
issue = Issue.order('id desc').first
|
|
| 97 |
assert_equal 'New test issue', issue.subject |
|
| 98 |
assert_equal 'New test issue description', issue.description |
|
| 99 |
assert_equal 'Low', issue.priority.name |
|
| 100 |
assert_equal 'CF1 value', issue.custom_field_value(field1) |
|
| 101 |
assert_equal 'CF2 value', issue.custom_field_value(field2) |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
def test_create_issue_with_watchers |
|
| 105 |
User.generate!(:firstname => 'Some', :lastname => 'Watcher') |
|
| 106 |
|
|
| 107 |
log_user('jsmith', 'jsmith')
|
|
| 108 |
visit '/projects/ecookbook/issues/new' |
|
| 109 |
fill_in 'Subject', :with => 'Issue with watchers' |
|
| 110 |
# Add a project member as watcher |
|
| 111 |
check 'Dave Lopper' |
|
| 112 |
# Search for another user |
|
| 113 |
click_link 'Search for watchers to add' |
|
| 114 |
within('form#new-watcher-form') do
|
|
| 115 |
assert page.has_content?('Some One')
|
|
| 116 |
fill_in 'user_search', :with => 'watch' |
|
| 117 |
assert page.has_no_content?('Some One')
|
|
| 118 |
check 'Some Watcher' |
|
| 119 |
click_button 'Add' |
|
| 120 |
end |
|
| 121 |
assert_difference 'Issue.count' do |
|
| 122 |
find('input[name=commit]').click
|
|
| 123 |
end |
|
| 124 |
|
|
| 125 |
issue = Issue.order('id desc').first
|
|
| 126 |
assert_equal ['Dave Lopper', 'Some Watcher'], issue.watcher_users.map(&:name).sort |
|
| 127 |
end |
|
| 128 |
|
|
| 129 |
def test_preview_issue_description |
|
| 130 |
log_user('jsmith', 'jsmith')
|
|
| 131 |
visit '/projects/ecookbook/issues/new' |
|
| 132 |
within('form#issue-form') do
|
|
| 133 |
fill_in 'Subject', :with => 'new issue subject' |
|
| 134 |
fill_in 'Description', :with => 'new issue description' |
|
| 135 |
click_link 'Preview' |
|
| 136 |
end |
|
| 137 |
find 'div#preview fieldset', :visible => true, :text => 'new issue description' |
|
| 138 |
assert_difference 'Issue.count' do |
|
| 139 |
find('input[name=commit]').click
|
|
| 140 |
end |
|
| 141 |
|
|
| 142 |
issue = Issue.order('id desc').first
|
|
| 143 |
assert_equal 'new issue description', issue.description |
|
| 144 |
end |
|
| 145 |
|
|
| 146 |
def test_update_issue_with_form_update |
|
| 147 |
field = IssueCustomField.create!( |
|
| 148 |
:field_format => 'string', |
|
| 149 |
:name => 'Form update CF', |
|
| 150 |
:is_for_all => true, |
|
| 151 |
:trackers => Tracker.find_all_by_name('Feature request')
|
|
| 152 |
) |
|
| 153 |
|
|
| 154 |
Role.non_member.add_permission! :edit_issues |
|
| 155 |
Role.non_member.remove_permission! :add_issues, :add_issue_notes |
|
| 156 |
|
|
| 157 |
log_user('someone', 'foo')
|
|
| 158 |
visit '/issues/1' |
|
| 159 |
assert page.has_no_content?('Form update CF')
|
|
| 160 |
|
|
| 161 |
page.first(:link, 'Update').click |
|
| 162 |
# the custom field should show up when changing tracker |
|
| 163 |
select 'Feature request', :from => 'Tracker' |
|
| 164 |
assert page.has_content?('Form update CF')
|
|
| 165 |
|
|
| 166 |
fill_in 'Form update', :with => 'CF value' |
|
| 167 |
assert_no_difference 'Issue.count' do |
|
| 168 |
page.first(:button, 'Submit').click |
|
| 169 |
end |
|
| 170 |
|
|
| 171 |
issue = Issue.find(1) |
|
| 172 |
assert_equal 'CF value', issue.custom_field_value(field) |
|
| 173 |
end |
|
| 174 |
|
|
| 175 |
def test_remove_issue_watcher_from_sidebar |
|
| 176 |
user = User.find(3) |
|
| 177 |
Watcher.create!(:watchable => Issue.find(1), :user => user) |
|
| 178 |
|
|
| 179 |
log_user('jsmith', 'jsmith')
|
|
| 180 |
visit '/issues/1' |
|
| 181 |
assert page.first('#sidebar').has_content?('Watchers (1)')
|
|
| 182 |
assert page.first('#sidebar').has_content?(user.name)
|
|
| 183 |
assert_difference 'Watcher.count', -1 do |
|
| 184 |
page.first('ul.watchers .user-3 a.delete').click
|
|
| 185 |
end |
|
| 186 |
assert page.first('#sidebar').has_content?('Watchers (0)')
|
|
| 187 |
assert page.first('#sidebar').has_no_content?(user.name)
|
|
| 188 |
end |
|
| 189 |
|
|
| 190 |
def test_watch_issue_via_context_menu |
|
| 191 |
log_user('jsmith', 'jsmith')
|
|
| 192 |
visit '/issues' |
|
| 193 |
find('tr#issue-1 td.updated_on').click
|
|
| 194 |
page.execute_script "$('tr#issue-1 td.updated_on').trigger('contextmenu');"
|
|
| 195 |
assert_difference 'Watcher.count' do |
|
| 196 |
within('#context-menu') do
|
|
| 197 |
click_link 'Watch' |
|
| 198 |
end |
|
| 199 |
end |
|
| 200 |
assert Issue.find(1).watched_by?(User.find_by_login('jsmith'))
|
|
| 201 |
end |
|
| 202 |
|
|
| 203 |
def test_bulk_watch_issues_via_context_menu |
|
| 204 |
log_user('jsmith', 'jsmith')
|
|
| 205 |
visit '/issues' |
|
| 206 |
find('tr#issue-1 input[type=checkbox]').click
|
|
| 207 |
find('tr#issue-4 input[type=checkbox]').click
|
|
| 208 |
page.execute_script "$('tr#issue-1 td.updated_on').trigger('contextmenu');"
|
|
| 209 |
assert_difference 'Watcher.count', 2 do |
|
| 210 |
within('#context-menu') do
|
|
| 211 |
click_link 'Watch' |
|
| 212 |
end |
|
| 213 |
end |
|
| 214 |
assert Issue.find(1).watched_by?(User.find_by_login('jsmith'))
|
|
| 215 |
assert Issue.find(4).watched_by?(User.find_by_login('jsmith'))
|
|
| 216 |
end |
|
| 217 |
end |
|
Also available in: Unified diff