qcourses 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rspec +1 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +75 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/config.ru +3 -0
- data/db/migrations/001_initial_database.rb +25 -0
- data/db/migrations/002_create_registrations.rb +29 -0
- data/demo_app.rb +11 -0
- data/lib/factories.rb +111 -0
- data/lib/qcourses/configuration.rb +35 -0
- data/lib/qcourses/controllers/base.rb +20 -0
- data/lib/qcourses/controllers/courses_controller.rb +16 -0
- data/lib/qcourses/controllers/events_controller.rb +54 -0
- data/lib/qcourses/controllers/registrations_controller.rb +51 -0
- data/lib/qcourses/controllers.rb +5 -0
- data/lib/qcourses/date_ex.rb +18 -0
- data/lib/qcourses/models/course.rb +30 -0
- data/lib/qcourses/models/course_repository.rb +90 -0
- data/lib/qcourses/models/event.rb +54 -0
- data/lib/qcourses/models/location.rb +17 -0
- data/lib/qcourses/models/registration.rb +36 -0
- data/lib/qcourses/models.rb +28 -0
- data/lib/qcourses/qcourses.rake.rb +21 -0
- data/lib/qcourses/renderers.rb +26 -0
- data/lib/qcourses/resource_paths.rb +45 -0
- data/lib/qcourses/string_ex.rb +5 -0
- data/lib/qcourses/view_helpers.rb +70 -0
- data/lib/qcourses/web_app.rb +29 -0
- data/lib/qcourses.rb +21 -0
- data/public/css/default.css +91 -0
- data/public/img/delete.gif +0 -0
- data/public/javascript/jquery-1.7.js +9300 -0
- data/public/javascript/jquery-1.7.min.js +4 -0
- data/public/javascript/qcourses.js +20 -0
- data/spec/factories_spec.rb +131 -0
- data/spec/qcourses/configuration_spec.rb +37 -0
- data/spec/qcourses/controllers/courses_controller_spec.rb +43 -0
- data/spec/qcourses/controllers/events_controller_spec.rb +100 -0
- data/spec/qcourses/controllers/registrations_controller_spec.rb +100 -0
- data/spec/qcourses/models/course_repository_spec.rb +147 -0
- data/spec/qcourses/models/event_spec.rb +74 -0
- data/spec/qcourses/models/location_spec.rb +19 -0
- data/spec/qcourses/models/registration_spec.rb +55 -0
- data/spec/qcourses/renderers_spec.rb +82 -0
- data/spec/qcourses/string_ex_spec.rb +11 -0
- data/spec/qcourses/view_helpers_spec.rb +90 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/factories.rb +24 -0
- data/spec/support/matchers.rb +61 -0
- data/spec/support/request_specs.rb +4 -0
- data/spec/support/test_files.rb +20 -0
- data/views/admin.haml +9 -0
- data/views/events/index.haml +18 -0
- data/views/events/new.haml +34 -0
- data/views/layout.haml +9 -0
- data/views/registration.haml +9 -0
- data/views/registrations/new.haml +37 -0
- data/views/registrations/participant.haml +12 -0
- data/views/registrations/success.haml +4 -0
- data/views/trainings/error.haml +1 -0
- data/views/trainings/index.haml +9 -0
- metadata +306 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'capybara/rspec'
|
3
|
+
|
4
|
+
module Qcourses
|
5
|
+
|
6
|
+
describe RegistrationsController, :type => :request do
|
7
|
+
let(:app) { Capybara.app = RegistrationsController }
|
8
|
+
let(:event) { Rubory.create :event }
|
9
|
+
|
10
|
+
describe 'get new' do
|
11
|
+
def url(id)
|
12
|
+
"/registrations/new/#{id}"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render a form" do
|
16
|
+
get url(event.id)
|
17
|
+
last_response.should be_ok
|
18
|
+
last_body.should have_selector("form#registrations[@method='post'][@action='/registrations/#{event.id}']")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return a 404 when event is not available" do
|
22
|
+
get url(event.id + 33334)
|
23
|
+
last_response.should be_not_found
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return a 404 when event is in the past" do
|
27
|
+
event.update :from => Date.today
|
28
|
+
get url(event.id)
|
29
|
+
last_response.should be_not_found
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'post registration' do
|
35
|
+
def url(id)
|
36
|
+
"/registrations/#{id}"
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:participants) { [valid_participant] }
|
40
|
+
let!(:valid_company) { Rubory.attributes_for :company }
|
41
|
+
let!(:valid_participant) { Rubory.attributes_for :employee }
|
42
|
+
let(:valid_registration) { { employee: participants, company: valid_company} }
|
43
|
+
|
44
|
+
context "on valid attributes" do
|
45
|
+
it "redirects to thank you" do
|
46
|
+
post url(event.id), valid_registration
|
47
|
+
last_response.should be_redirect
|
48
|
+
follow_redirect!
|
49
|
+
last_body.should have_selector(".thanks")
|
50
|
+
end
|
51
|
+
it "creates a registration" do
|
52
|
+
expect { post url(event.id), valid_registration }.to change { Registration.count }.by(1)
|
53
|
+
end
|
54
|
+
it "creates a company" do
|
55
|
+
expect { post url(event.id), valid_registration }.to change { Company.count }.by(1)
|
56
|
+
end
|
57
|
+
it "creates an employee" do
|
58
|
+
expect { post url(event.id), valid_registration }.to change { Employee.count }.by(1)
|
59
|
+
end
|
60
|
+
context "with more employees" do
|
61
|
+
let!(:another_participant) { Rubory.attributes_for :employee }
|
62
|
+
let(:participants) { [valid_participant, another_participant] }
|
63
|
+
it "creates more registrations" do
|
64
|
+
expect { post url(event.id), valid_registration }.to change { Registration.count }.by(2)
|
65
|
+
end
|
66
|
+
it "creates a company" do
|
67
|
+
expect { post url(event.id), valid_registration }.to change { Company.count }.by(1)
|
68
|
+
end
|
69
|
+
it "creates more employees" do
|
70
|
+
expect { post url(event.id), valid_registration }.to change { Employee.count }.by(2)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context "problems with event" do
|
75
|
+
it "should return a 404 when event is not available" do
|
76
|
+
post url(event.id + 33334)
|
77
|
+
last_response.should be_not_found
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return a 404 when event is in the past" do
|
81
|
+
event.update :from => Date.today
|
82
|
+
post url(event.id)
|
83
|
+
last_response.should be_not_found
|
84
|
+
end
|
85
|
+
end
|
86
|
+
context "problems with attributes" do
|
87
|
+
context "participant invalid" do
|
88
|
+
it "shows error on attribute" do
|
89
|
+
valid_participant[:email] = ''
|
90
|
+
post url(event.id), valid_registration
|
91
|
+
last_response.should be_ok
|
92
|
+
last_body.should have_selector("form input.error[name='employee[][email]']")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Qcourses
|
4
|
+
class StubCourseFileOpener
|
5
|
+
def initialize(files = {})
|
6
|
+
@files=files
|
7
|
+
@files.default = ''
|
8
|
+
end
|
9
|
+
def define_file(filename, content)
|
10
|
+
@files[filename] = content
|
11
|
+
end
|
12
|
+
def open(name, &block)
|
13
|
+
StringIO.open(@files[name], &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class StubDirectory
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@results = {}
|
21
|
+
@results.default = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def glob(pattern)
|
25
|
+
@results[pattern]
|
26
|
+
end
|
27
|
+
|
28
|
+
def pattern(pattern, options = {returns: []})
|
29
|
+
@results[pattern] = options[:returns]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'CourseRepository' do
|
34
|
+
let(:directory) { StubDirectory.new }
|
35
|
+
let(:file_opener) { StubCourseFileOpener.new('first_course.mdown' => "Name: first course",
|
36
|
+
'second_course.mdown' => "Name: second course") }
|
37
|
+
let(:repository) { CourseRepository.new(directory, file_opener) }
|
38
|
+
let(:first_course) { repository.all.first }
|
39
|
+
|
40
|
+
describe 'class methods' do
|
41
|
+
it "can't be used when not configured yet" do
|
42
|
+
expect { CourseRepository.all }.to raise_exception CourseRepository::Error
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'all' do
|
47
|
+
context "detecting course files" do
|
48
|
+
it "returns a course for all the **/courses/*.mdown files" do
|
49
|
+
directory.pattern("**/courses/*.mdown", returns: ['first_course.mdown', 'second_course.mdown'])
|
50
|
+
|
51
|
+
repository.all.should == [ Course.new(identification: 'first_course', name: 'first course'),
|
52
|
+
Course.new(identification: 'second_course', name: 'second_course') ]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "returns a course for all the **/cursussen/*.mdown files " do
|
56
|
+
directory.pattern("**/cursussen/*.mdown", returns: ['first_course.mdown', 'second_course.mdown'])
|
57
|
+
|
58
|
+
repository.all.should == [ Course.new(identification: 'first_course', name: 'first course'),
|
59
|
+
Course.new(identification: 'second_course', name: 'second_course') ]
|
60
|
+
end
|
61
|
+
it "ignores duplication in courses" do
|
62
|
+
directory.pattern("**/courses/*.mdown", returns: ['first_course.mdown'])
|
63
|
+
directory.pattern("**/cursussen/*.mdown", returns: ['first_course.mdown'])
|
64
|
+
|
65
|
+
repository.all.should == [ Course.new(identification: 'first_course', name: 'first course')]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "uses just the actual filenames as the course name - not the complete path" do
|
69
|
+
directory.pattern("**/cursussen/*.mdown", returns: ['en/courses/first_course.mdown'])
|
70
|
+
file_opener.define_file('en/courses/first_course.mdown', "Name: first course")
|
71
|
+
|
72
|
+
first_course.should == Course.new(identification: 'first_course', name: 'first course')
|
73
|
+
end
|
74
|
+
|
75
|
+
it "skips index files" do
|
76
|
+
directory.pattern("**/cursussen/*.mdown", returns: ['en/courses/index.mdown'])
|
77
|
+
file_opener.define_file('en/courses/index.mdown', "Name: first course")
|
78
|
+
|
79
|
+
repository.all.should == []
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "parsing the course" do
|
84
|
+
before do
|
85
|
+
directory.pattern("**/cursussen/*.mdown", returns: ['first_course.mdown'])
|
86
|
+
end
|
87
|
+
def course_file_contains(content)
|
88
|
+
file_opener.define_file('first_course.mdown', content)
|
89
|
+
end
|
90
|
+
it "accepts more headers after name" do
|
91
|
+
course_file_contains(%Q{Name: first course
|
92
|
+
Category: training})
|
93
|
+
first_course.name.should == 'first course'
|
94
|
+
end
|
95
|
+
it "accepts more headers before name" do
|
96
|
+
course_file_contains(%Q{Category: training
|
97
|
+
Name: first course })
|
98
|
+
first_course.name.should == 'first course'
|
99
|
+
end
|
100
|
+
it "ignores headers after first empty line" do
|
101
|
+
course_file_contains(%Q{Category: training
|
102
|
+
|
103
|
+
Name: first course })
|
104
|
+
first_course.name.should == 'first_course'
|
105
|
+
end
|
106
|
+
it "ignores lines after first line that does not contain colon" do
|
107
|
+
course_file_contains(%Q{Category training
|
108
|
+
Name: first course })
|
109
|
+
first_course.name.should == 'first_course'
|
110
|
+
end
|
111
|
+
context "name" do
|
112
|
+
it "uses identification as the name if the name is not defined" do
|
113
|
+
course_file_contains('')
|
114
|
+
first_course.name.should == 'first_course'
|
115
|
+
end
|
116
|
+
it "reads the name from the Name header" do
|
117
|
+
course_file_contains('Name: first course')
|
118
|
+
first_course.name.should == 'first course'
|
119
|
+
end
|
120
|
+
it "accepts colon in name" do
|
121
|
+
course_file_contains('Name: first: course')
|
122
|
+
first_course.name.should == 'first: course'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
context "subtitle" do
|
126
|
+
it "is empty if the subtitle is not defined" do
|
127
|
+
course_file_contains('')
|
128
|
+
first_course.subtitle.should == ''
|
129
|
+
end
|
130
|
+
it "reads the subtitle from the Subtitle header" do
|
131
|
+
course_file_contains('Subtitle: learn stuff!')
|
132
|
+
first_course.subtitle.should == 'learn stuff!'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
describe "find(course_identification)" do
|
138
|
+
it "returns the associated course" do
|
139
|
+
repository.find('first_course').should == first_course
|
140
|
+
end
|
141
|
+
it "returns nil if course does not exist" do
|
142
|
+
repository.find('non-existing').should be_nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Qcourses
|
4
|
+
describe Event do
|
5
|
+
|
6
|
+
|
7
|
+
describe "storing events" do
|
8
|
+
subject { Event.new valid_attributes }
|
9
|
+
context "location as a string" do
|
10
|
+
let(:valid_attributes) { { course_id: 'agile_engineering', from: "10-04-2012", to: "13-04-2012", location: "Tilburg"} }
|
11
|
+
|
12
|
+
it "can be stored and loaded" do
|
13
|
+
subject.save
|
14
|
+
reloaded = Event[subject.id]
|
15
|
+
reloaded.should == subject
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should fail on empty location" do
|
19
|
+
subject.should reject_values("").for(:location)
|
20
|
+
end
|
21
|
+
it { should accept_values("2012-04-01", "10-10-2012").for(:from) }
|
22
|
+
it { should accept_values("2012-04-01", "10-10-2012").for(:to) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "attributes" do
|
27
|
+
|
28
|
+
let(:subject) {Event.new(course_id: 'ae', from: "10-04-2012", to: "12-04-2012") }
|
29
|
+
its(:from) { should be_a(Time) }
|
30
|
+
its(:from) { should == Time.parse("10-04-2012") }
|
31
|
+
its(:to) { should be_a(Time) }
|
32
|
+
|
33
|
+
|
34
|
+
describe "from course" do
|
35
|
+
let(:course_repository) { CourseRepository.in_memory }
|
36
|
+
let!(:course) { course_repository.create_course :identification => 'ae', :name => "AE", :subtitle => 'learn ae' }
|
37
|
+
its(:name) { should == course.name }
|
38
|
+
its(:subtitle) { should == course.subtitle }
|
39
|
+
|
40
|
+
context 'when course not available' do
|
41
|
+
before { subject.course_id = 'removed_id' }
|
42
|
+
its(:name) { should == 'removed course (removed_id)' }
|
43
|
+
its(:subtitle) { should == '' }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'location' do
|
48
|
+
it "is converted to a location object" do
|
49
|
+
subject.location = "Tilburg"
|
50
|
+
subject.location.should be_a(Location)
|
51
|
+
subject.location.name.should == 'Tilburg'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "_name attribute delegates to location or is unknown" do
|
55
|
+
subject.location_name.should == 'unknown'
|
56
|
+
subject.location = "Tilburg"
|
57
|
+
subject.location_name.should == subject.location.name
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with same name exists" do
|
61
|
+
let!(:existing_location) { Location.create(name: "Tilburg") }
|
62
|
+
it "existing location is used when available" do
|
63
|
+
subject.location = "Tilburg"
|
64
|
+
subject.location.should == existing_location
|
65
|
+
end
|
66
|
+
it "existing location is located case insensitive" do
|
67
|
+
subject.location = "tilburG"
|
68
|
+
subject.location.should == existing_location
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Qcourses
|
4
|
+
describe Location do
|
5
|
+
describe 'storing locations' do
|
6
|
+
subject { Location.new valid_attributes }
|
7
|
+
let(:valid_attributes) {{name: 'Qwan Tilburg', address: "Gondelstraat", postal_code: "2323 AA", city: "Tilburg", url:"/contact" }}
|
8
|
+
|
9
|
+
it "should be able to save an read back" do
|
10
|
+
subject.save
|
11
|
+
read_back = Location[subject.id]
|
12
|
+
read_back.should == subject
|
13
|
+
end
|
14
|
+
|
15
|
+
it {should reject_values(nil, "").for(:name)}
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Qcourses
|
4
|
+
describe Registration do
|
5
|
+
describe "saving" do
|
6
|
+
let(:employee) { Rubory.create :employee }
|
7
|
+
let(:event) { Rubory.create :event }
|
8
|
+
let(:subject) {Registration.new(employee: employee, event: event)}
|
9
|
+
|
10
|
+
it "can be saved and read back" do
|
11
|
+
subject.save
|
12
|
+
reloaded = Registration[subject.id]
|
13
|
+
reloaded.should == subject
|
14
|
+
end
|
15
|
+
|
16
|
+
it { should reject_values(nil).for(:employee) }
|
17
|
+
it { should reject_values(nil).for(:event) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe Employee do
|
22
|
+
describe "saving" do
|
23
|
+
let(:subject) { Rubory.build :employee }
|
24
|
+
it "can be saved and read back" do
|
25
|
+
subject.save
|
26
|
+
reloaded = Employee[subject.id]
|
27
|
+
reloaded.should == subject
|
28
|
+
end
|
29
|
+
|
30
|
+
it { should reject_values(nil, "").for(:name) }
|
31
|
+
it { should reject_values(
|
32
|
+
nil, "",
|
33
|
+
"rob@", "@mo.nl", "123.nl", "123@nl", "aaa.123.nl", "aaa.123@nl").for(:email) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe Company do
|
38
|
+
describe "saving" do
|
39
|
+
let(:subject) { Rubory.build :company }
|
40
|
+
it "can be saved and read back" do
|
41
|
+
subject.save
|
42
|
+
reloaded = Company[subject.id]
|
43
|
+
reloaded.should == subject
|
44
|
+
end
|
45
|
+
|
46
|
+
it { should reject_values(nil, "").for(:name) }
|
47
|
+
it { should reject_values(nil, "").for(:contact_person) }
|
48
|
+
it { should reject_values(nil, "").for(:invoice_address) }
|
49
|
+
it { should reject_values(nil, "").for(:postal_code) }
|
50
|
+
it { should reject_values(nil, "").for(:city) }
|
51
|
+
it { should reject_values( nil, "", "rob@", "@mo.nl", "123.nl", "123@nl", "aaa.123.nl", "aaa.123@nl")
|
52
|
+
.for(:contact_email) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Qcourses
|
4
|
+
describe Renderers do
|
5
|
+
require 'sinatra'
|
6
|
+
let(:template_cache) { Tilt::Cache.new }
|
7
|
+
include Sinatra::Templates
|
8
|
+
include Renderers
|
9
|
+
let(:local_root) { spec_path 'files/local' }
|
10
|
+
let(:root) { spec_path "files/remote" }
|
11
|
+
let(:views) { 'views' }
|
12
|
+
|
13
|
+
after do
|
14
|
+
delete_files('files')
|
15
|
+
end
|
16
|
+
|
17
|
+
def html_for(result)
|
18
|
+
Capybara.string(result)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'haml' do
|
22
|
+
it "uses the local path if it exists" do
|
23
|
+
create_file('files/local/views/view.haml', '%h1 local view')
|
24
|
+
html_for(haml(:'view')).should have_selector('h1', :text => 'local view')
|
25
|
+
end
|
26
|
+
it "uses the remote path if it exists" do
|
27
|
+
create_file('files/remote/views/view.haml', '%h1 remote view')
|
28
|
+
html_for(haml(:'view')).should have_selector('h1', :text => 'remote view')
|
29
|
+
end
|
30
|
+
it "remote path overrides local if it exists" do
|
31
|
+
create_file('files/local/views/view.haml', '%h1 local view')
|
32
|
+
create_file('files/remote/views/view.haml', '%h1 remote view')
|
33
|
+
html_for(haml(:'view')).should have_selector('h1', :text => 'remote view')
|
34
|
+
end
|
35
|
+
it "uses the string if haml is a string" do
|
36
|
+
html_for(haml('%h1 inline view')).should have_selector('h1', :text => 'inline view')
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with layout available locally" do
|
40
|
+
before do
|
41
|
+
create_file('files/local/views/view.haml', '%h1 local view')
|
42
|
+
create_file('files/local/views/layout.haml', "%h1 local layout \n=yield")
|
43
|
+
end
|
44
|
+
it "renders no layout when layout specified to be false" do
|
45
|
+
html_for(haml(:'view', layout: false )).should_not have_selector( 'h1', :text => 'local layout')
|
46
|
+
end
|
47
|
+
it "renders layout when layout specified to be true" do
|
48
|
+
html_for(haml(:'view', layout: true)).should have_selector( 'h1', :text => 'local layout')
|
49
|
+
end
|
50
|
+
it "renders layout when layout specified by name" do
|
51
|
+
html_for(haml(:'view', layout: :layout)).should have_selector( 'h1', :text => 'local layout')
|
52
|
+
end
|
53
|
+
it "complains about layout not to exist when layout is not available" do
|
54
|
+
expect { haml(:'view', layout: :admini) }.to raise_exception(Errno::ENOENT)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with layout available remotely" do
|
59
|
+
before do
|
60
|
+
create_file('files/local/views/view.haml', '%h1 local view')
|
61
|
+
create_file('files/remote/views/layout.haml', "%h1 remote layout \n=yield")
|
62
|
+
end
|
63
|
+
it "renders no layout when layout specified to be false" do
|
64
|
+
html_for(haml(:'view', layout: false )).should_not have_selector( 'h1', :text => 'remote layout')
|
65
|
+
end
|
66
|
+
it "renders layout when layout specified to be true" do
|
67
|
+
html_for(haml(:'view', layout: true )).should have_selector( 'h1', :text => 'remote layout')
|
68
|
+
end
|
69
|
+
it "renders layout when layout specified by name" do
|
70
|
+
html_for(haml(:'view', layout: :layout)).should have_selector( 'h1', :text => 'remote layout')
|
71
|
+
end
|
72
|
+
it "complains about layout not to exist when layout is not available" do
|
73
|
+
expect { haml(:'view', layout: :admin) }.to raise_exception(Errno::ENOENT)
|
74
|
+
end
|
75
|
+
it "falls back to local layout when remote does not exist" do
|
76
|
+
create_file('files/local/views/admin.haml', "%h1 local admin layout \n=yield")
|
77
|
+
html_for(haml(:'view', layout: :admin)).should have_selector( 'h1', :text => 'local admin layout')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
describe "underscore" do
|
5
|
+
it "underscores a camel cased string" do
|
6
|
+
"Event".underscore.should == "event"
|
7
|
+
"EventName".underscore.should == "event_name"
|
8
|
+
"EEEventName".underscore.should == "ee_event_name"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Qcourses
|
4
|
+
describe ViewHelpers do
|
5
|
+
require 'sinatra'
|
6
|
+
let(:template_cache) { Tilt::Cache.new }
|
7
|
+
include Sinatra::Templates
|
8
|
+
include ViewHelpers
|
9
|
+
|
10
|
+
describe 'human_period' do
|
11
|
+
let(:event) { Event.new(from: "30-11-2012", to: "02-12-2013") }
|
12
|
+
let(:subject) { human_period(event.from, event.to) }
|
13
|
+
|
14
|
+
it "formats both dates short with a hyphen inbetween" do
|
15
|
+
subject.should == "30/11/2012 - 2/12/2013"
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when dates are in same year" do
|
19
|
+
before { event.to = "02-12-2012" }
|
20
|
+
it "formats both dates with a hyphen when dates are in different months" do
|
21
|
+
subject.should == '30/11 - 2/12 2012'
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when dates are in same month" do
|
25
|
+
it "formats both dates with a hyphen" do
|
26
|
+
event.from = "1-12-2012"
|
27
|
+
subject.should == '1 - 2 December 2012'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "input_for" do
|
34
|
+
def html_for(result)
|
35
|
+
Capybara.string(result)
|
36
|
+
end
|
37
|
+
before do
|
38
|
+
@employee = Employee.new :name => 'gijs'
|
39
|
+
@employee.valid?
|
40
|
+
end
|
41
|
+
describe "on an existing object" do
|
42
|
+
it 'creates an input tag' do
|
43
|
+
html_for(input_for(:employee, :name)).should have_selector("input[type='text'][name='employee[name]'][value='gijs']")
|
44
|
+
html_for(input_for(@employee, :name)).should have_selector("input[type='text'][name='employee[name]'][value='gijs']")
|
45
|
+
end
|
46
|
+
it 'creates an an error class if attribute has error' do
|
47
|
+
result = html_for(input_for(:employee, :email))
|
48
|
+
result.should have_selector("input[type='text'][name='employee[email]'][class='error']")
|
49
|
+
result.should have_selector("span[class='error']", :text => @employee.errors.on(:email).first)
|
50
|
+
end
|
51
|
+
it 'can suppress error messagee if attribute has error' do
|
52
|
+
result = html_for(input_for(:employee, :email, :suppress_error_messages => true))
|
53
|
+
result.should have_selector("input[type='text'][name='employee[email]'][class='error']")
|
54
|
+
result.should_not have_selector("span[class='error']", :text => @employee.errors.on(:email).first)
|
55
|
+
end
|
56
|
+
it 'can have a class' do
|
57
|
+
html_for(input_for(:employee, :name, :class => 'short')).should have_selector("input[name='employee[name]'][class='short']")
|
58
|
+
html_for(input_for(:employee, :email, :class => 'short')).should have_selector("input[name='employee[email]'][class='error short']")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
describe "on an array object" do
|
62
|
+
it "creates an input tag with an extra [] inthe name" do
|
63
|
+
html_for(input_for(:employee, :name, :array_element=>true)).should have_selector("input[type='text'][name='employee[][name]'][value='gijs']")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
describe "text_field_for" do
|
68
|
+
def html_for(result)
|
69
|
+
Capybara.string(result)
|
70
|
+
end
|
71
|
+
before do
|
72
|
+
@company = Company.new
|
73
|
+
end
|
74
|
+
describe "on an existing object" do
|
75
|
+
it 'creates a textarea tag' do
|
76
|
+
html_for(textarea_for(:company, :invoice_address)).should have_selector("textarea[name='company[invoice_address]']")
|
77
|
+
html_for(textarea_for(@company, :invoice_address)).should have_selector("textarea[name='company[invoice_address]']")
|
78
|
+
end
|
79
|
+
it "contains the value" do
|
80
|
+
@company.invoice_address = "Gondelstraat\nTilburg"
|
81
|
+
html_for(textarea_for(:company, :invoice_address)).should have_selector("textarea[name='company[invoice_address]']", :text=>@company.invoice_address)
|
82
|
+
end
|
83
|
+
it 'creates an an error class if attribute has error' do
|
84
|
+
@company.errors.add(:invoice_address, "wrong address")
|
85
|
+
html_for(textarea_for(:company, :invoice_address)).should have_selector("textarea[name='company[invoice_address]'][class='error']")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
ENV['RACK_ENV'] = 'test'
|
6
|
+
|
7
|
+
require 'rspec'
|
8
|
+
require 'qcourses'
|
9
|
+
require 'rack/test'
|
10
|
+
require 'capybara'
|
11
|
+
|
12
|
+
Qcourses.configure do |config|
|
13
|
+
config.root = File.expand_path('..',File.dirname(__FILE__))
|
14
|
+
end
|
15
|
+
|
16
|
+
# Requires supporting files with custom matchers and macros, etc,
|
17
|
+
# in ./support/ and its subdirectories.
|
18
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
19
|
+
|
20
|
+
class RSpec::Core::ExampleGroup
|
21
|
+
# Setting an around filter globally doesn't appear to work in 2.7 (and maybe other versions),
|
22
|
+
# so set one up for each subclass.
|
23
|
+
def self.inherited(subclass)
|
24
|
+
super
|
25
|
+
subclass.around do |example|
|
26
|
+
Qcourses::CourseRepository.destroy
|
27
|
+
Sequel::Model.db.transaction(:rollback=>:always){example.call}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec.configure do |config|
|
33
|
+
config.include Rack::Test::Methods
|
34
|
+
config.include TestFiles
|
35
|
+
config.filter_run_excluding :broken => true
|
36
|
+
config.filter_run :focus => true
|
37
|
+
config.run_all_when_everything_filtered = true
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'factories'
|
2
|
+
require 'qcourses/date_ex'
|
3
|
+
Rubory.use_default_module('Qcourses')
|
4
|
+
|
5
|
+
Rubory.define(:company) do
|
6
|
+
name { |n| "company_#{n}" }
|
7
|
+
contact_person "some contact"
|
8
|
+
contact_email { |n| "contact_#{n}@email.com" }
|
9
|
+
invoice_address "some address"
|
10
|
+
postal_code "1234 AA"
|
11
|
+
city "Amsterdam"
|
12
|
+
end
|
13
|
+
|
14
|
+
Rubory.define(:employee) do
|
15
|
+
name "some employee"
|
16
|
+
email { |n| "email_#{n}@qwan.it" }
|
17
|
+
associate :company
|
18
|
+
end
|
19
|
+
|
20
|
+
Rubory.define(:event) do
|
21
|
+
from { Date.tomorrow }
|
22
|
+
to { Date.tomorrow.tomorrow }
|
23
|
+
location "Tilburg"
|
24
|
+
end
|