rubyuw 0.99.2 → 0.99.3

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/README.md CHANGED
@@ -52,9 +52,9 @@ The following is a quick and simple example:
52
52
 
53
53
  ## Documentation
54
54
 
55
- You may view all documentation [at the following URL](http://mitchellh.github.com/RubyUW/):
55
+ You may view all documentation [at the following URL](http://mitchellh.github.com/rubyuw/):
56
56
 
57
- http://mitchellh.github.com/RubyUW/
57
+ [http://mitchellh.github.com/rubyuw/](http://mitchellh.github.com/rubyuw/)
58
58
 
59
59
  ## Testing
60
60
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.99.2
1
+ 0.99.3
@@ -0,0 +1,192 @@
1
+ module RubyUW
2
+ # RubyUW::Curriculum is used to gather a list of courses offered by
3
+ # a specific curriculum within the University of Washington. This
4
+ # is opposite of {RubyUW::SLN} which pulls data for a single SLN.
5
+ # With RubyUW::Curriculum, you can answer the following sorts of
6
+ # questions:
7
+ #
8
+ # * What courses are offered within the "Chemistry" curriculum?
9
+ # * What courses are offered within the "BIO A" curriculum?
10
+ # * What courses are offered for "CHEM 142?" This would return
11
+ # all the lectures, labs, and quiz sections associated with a
12
+ # specific course.
13
+ #
14
+ # <b>Does _not_ require authentication with {RubyUW::Base} for use!</b>
15
+ class Curriculum
16
+ class <<self
17
+ # Find all courses within a specific curriculum or course.
18
+ # The class itself determines, based on the format of the
19
+ # curriculum parameter, whether the user intends to search
20
+ # for an entire curriculum or specific course. This search
21
+ # can be verified in the resulting hash's <pre>:type</pre>
22
+ # value.
23
+ #
24
+ # = Examples:
25
+ #
26
+ # RubyUW::Curriculum.find("Chemistry", "WIN2010")
27
+ #
28
+ # RubyUW::Curriculum.find("BIO A", "WIN2010")
29
+ #
30
+ # RubyUW::Curriculum.find("CHEM 142", "WIN2010")
31
+ #
32
+ # = Return Format
33
+ #
34
+ # The method returns a hash which depending on your search is
35
+ # returned in one of two formats. Specific documentation for this
36
+ # will come soon. For now I recommend simply inspecting the hash
37
+ # for yourself, as it is fairly simple.
38
+ #
39
+ # @param [String] curriculum The curriculum or course to search
40
+ # @param [String] term The term (quarter) to search in.
41
+ # @return [Hash]
42
+ def find(curriculum, term)
43
+ curriculum = curriculum.strip # So we don't modify the original variable
44
+ type = !!(curriculum =~ /^(.+?)(\d+)$/) ? :course : :curriculum
45
+ send("find_#{type}", curriculum, term)
46
+ end
47
+
48
+ protected
49
+
50
+ def find_curriculum(curriculum, term)
51
+ page = Base.connection.get("http://www.washington.edu/students/timeschd/#{term}/")
52
+ raise Errors::QuarterDoesNotExistError.new if !quarter_exists?(page)
53
+
54
+ curriculum_page = extract_curriculum_url(curriculum, page)
55
+ raise Errors::CurriculumDoesNotExistError.new if curriculum_page.nil?
56
+
57
+ curriculum_page = Base.connection.get("http://www.washington.edu/students/timeschd/#{term}/#{curriculum_page}")
58
+ raise Errors::CurriculumDoesNotExistError.new if !curriculum_exists?(page)
59
+
60
+ return {
61
+ :type => :curriculum,
62
+ :courses => extract_courses(curriculum_page)
63
+ }
64
+ end
65
+
66
+ def find_course(raw_curriculum, term)
67
+ curriculum, course = parse_curriculum(raw_curriculum)
68
+ curriculum_data = find_curriculum(curriculum, term)
69
+ courses = find_course_in_data(course, curriculum_data)
70
+ raise Errors::CourseDoesNotExistError.new(curriculum, course, raw_curriculum) if courses.nil?
71
+
72
+ return {
73
+ :type => :course,
74
+ :courses => courses
75
+ }
76
+ end
77
+
78
+ def find_course_in_data(course, data)
79
+ data[:courses].each do |course_data, listing|
80
+ next unless course_data[:name] =~ /#{course}$/
81
+
82
+ return listing
83
+ end
84
+
85
+ return nil
86
+ end
87
+
88
+ def extract_courses(page)
89
+ nodes = page.search("//table[@bgcolor=\"#d3d3d3\"][1]//following-sibling::table")
90
+
91
+ courses = {}
92
+ current_course = []
93
+
94
+ nodes.each do |node|
95
+ if node[:bgcolor] == "#99ccff"
96
+ # If its a course header, start putting course listings under that
97
+ # course header
98
+ current_course = []
99
+ courses[extract_course_header(node)] = current_course
100
+ else
101
+ # Otherwise we just extract the course listing and stuff it under
102
+ # the the current course
103
+ current_course.push(extract_course_listing(node))
104
+ end
105
+ end
106
+
107
+ courses
108
+ end
109
+
110
+ def extract_course_header(node)
111
+ Base.extract(node, ".//td[@width=\"50%\"]//a", [:name, :title])
112
+ end
113
+
114
+ def extract_course_listing(node)
115
+ pre_node = node.search(".//pre")
116
+ pre_text = pre_node.inner_text.gsub("\302\240", "").gsub("\n", "").strip.chomp
117
+
118
+ # Ugly regex to match the <pre> text to extract the proper information for the
119
+ # course.
120
+ raise Errors::CourseListingMatchError.new(pre_text) unless pre_text =~ /^((IS\s+)|(Restr\s+))?>?([0-9]+)\s([A-Z]{1,2})\s(.+?)\s(.+)/i #\s(([A-Z]+)\s(.+?))\s(([A-Z]+)\s([0-9]+))/i
121
+
122
+ {
123
+ :sln => $4.to_s,
124
+ :section => $5.to_s,
125
+ :type => ($6.to_i > 0) ? "LC" : $3.to_s
126
+ }
127
+ end
128
+
129
+ def quarter_exists?(page)
130
+ # Time Schedule - No Courses Offered
131
+ return !(page.body.to_s =~ /Time Schedule - No Courses Offered/i)
132
+ end
133
+
134
+ # For now, unless MyUW changes, these are equivalent
135
+ alias :curriculum_exists? :quarter_exists?
136
+
137
+ def extract_curriculum_url(curriculum, page)
138
+ links = page.search("a")
139
+
140
+ links.each do |link|
141
+ text = link.inner_text.gsub("\302\240", " ").to_s
142
+
143
+ return link[:href].to_s if text =~ /^#{curriculum} \((.+?)\)$/i ||
144
+ text =~ /^(.+?) \(#{curriculum}\)$/i
145
+ end
146
+
147
+ return nil
148
+ end
149
+
150
+ def parse_curriculum(curriculum)
151
+ return nil unless curriculum =~ /^(.+?)\s+(\d+)$/
152
+ return $1, $2
153
+ end
154
+ end
155
+ end
156
+
157
+ module Errors
158
+ # An error indicating that a quarter does not exist in the time schedule
159
+ class QuarterDoesNotExistError < Exception; end
160
+
161
+ # An error indicating that the curriculum requested does not exist
162
+ class CurriculumDoesNotExistError < Exception; end
163
+
164
+ # An error indicating that the course listing didn't match the format
165
+ class CourseListingMatchError < Exception
166
+ attr_reader :listing_text
167
+
168
+ def initialize(listing_text, message = nil)
169
+ @listing_text = listing_text
170
+
171
+ message = "Failed to match: #{@listing_text}" if message.nil?
172
+ super(message)
173
+ end
174
+ end
175
+
176
+ # An error indicating that a course does not exist
177
+ class CourseDoesNotExistError < Exception
178
+ attr_reader :curriculum
179
+ attr_reader :course
180
+ attr_reader :original
181
+
182
+ def initialize(curriculum, course, original, message = nil)
183
+ @curriculum = curriculum
184
+ @course = course
185
+ @original = original
186
+
187
+ message = "Course does not exist: (#{curriculum}, #{course}) from \"#{original}\""
188
+ super(message)
189
+ end
190
+ end
191
+ end
192
+ end
data/lib/rubyuw.rb CHANGED
@@ -4,6 +4,7 @@ require 'nokogiri'
4
4
  require 'rubyuw/errors'
5
5
  require 'rubyuw/base'
6
6
  require 'rubyuw/connection'
7
+ require 'rubyuw/curriculum'
7
8
  require 'rubyuw/sln'
8
9
  require 'rubyuw/curriculum_enrollment'
9
10
  require 'rubyuw/schedule'
data/rubyuw.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rubyuw}
8
- s.version = "0.99.2"
8
+ s.version = "0.99.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Mitchell Hashimoto"]
12
- s.date = %q{2009-12-06}
12
+ s.date = %q{2010-01-17}
13
13
  s.description = %q{Library which provides a ruby interface to the University of Washington student portal.}
14
14
  s.email = %q{mitchell.hashimoto@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -23,17 +23,20 @@ Gem::Specification.new do |s|
23
23
  "lib/rubyuw.rb",
24
24
  "lib/rubyuw/base.rb",
25
25
  "lib/rubyuw/connection.rb",
26
+ "lib/rubyuw/curriculum.rb",
26
27
  "lib/rubyuw/curriculum_enrollment.rb",
27
28
  "lib/rubyuw/errors.rb",
28
29
  "lib/rubyuw/schedule.rb",
29
30
  "lib/rubyuw/sln.rb",
30
31
  "rubyuw.gemspec",
31
32
  "test/live/curriculum_enrollment_test.rb",
33
+ "test/live/curriculum_test.rb",
32
34
  "test/live/sln_test.rb",
33
35
  "test/live/test_helper.rb",
34
36
  "test/mocked/base_test.rb",
35
37
  "test/mocked/connection_test.rb",
36
38
  "test/mocked/curriculum_enrollment_test.rb",
39
+ "test/mocked/curriculum_test.rb",
37
40
  "test/mocked/fixture_pages/bad_request.html",
38
41
  "test/mocked/fixture_pages/curric_courses.html",
39
42
  "test/mocked/fixture_pages/curric_no_courses.html",
@@ -43,6 +46,7 @@ Gem::Specification.new do |s|
43
46
  "test/mocked/fixture_pages/no_form_login.html",
44
47
  "test/mocked/fixture_pages/no_login_button.html",
45
48
  "test/mocked/fixture_pages/not_logged_in_relay.html",
49
+ "test/mocked/fixture_pages/quarter_time_schedule.html",
46
50
  "test/mocked/fixture_pages/registration.html",
47
51
  "test/mocked/fixture_pages/registration_error.html",
48
52
  "test/mocked/fixture_pages/registration_error_no_rows.html",
@@ -58,6 +62,7 @@ Gem::Specification.new do |s|
58
62
  "test/mocked/fixture_pages/sln_no_course_notes.html",
59
63
  "test/mocked/fixture_pages/sln_no_enrollment_info.html",
60
64
  "test/mocked/fixture_pages/sln_status.html",
65
+ "test/mocked/fixture_pages/time_schedule_chem.html",
61
66
  "test/mocked/fixture_pages/welcome.html",
62
67
  "test/mocked/schedule_test.rb",
63
68
  "test/mocked/sln_test.rb",
@@ -71,11 +76,13 @@ Gem::Specification.new do |s|
71
76
  s.summary = %q{Library which provides a ruby interface to the University of Washington student portal.}
72
77
  s.test_files = [
73
78
  "test/live/curriculum_enrollment_test.rb",
79
+ "test/live/curriculum_test.rb",
74
80
  "test/live/sln_test.rb",
75
81
  "test/live/test_helper.rb",
76
82
  "test/mocked/base_test.rb",
77
83
  "test/mocked/connection_test.rb",
78
84
  "test/mocked/curriculum_enrollment_test.rb",
85
+ "test/mocked/curriculum_test.rb",
79
86
  "test/mocked/schedule_test.rb",
80
87
  "test/mocked/sln_test.rb",
81
88
  "test/mocked/test_helper.rb"
@@ -0,0 +1,103 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class LiveCurriculumTest < Test::Unit::TestCase
4
+ setup do
5
+ @term = "WIN2010"
6
+ end
7
+
8
+ context "live curriculum listing grabbing" do
9
+ context "checking parsing of curriculums" do
10
+ setup do
11
+ @valid_curriculums = ["CHEM", "Chemistry", " BIO A "]
12
+ @invalid_curriculums = ["Underwater Weaving", " Chmeistry"]
13
+ end
14
+
15
+ should "not raise any errors on valid curriculums" do
16
+ @valid_curriculums.each do |curriculum|
17
+ assert_nothing_raised {
18
+ result = RubyUW::Curriculum.find(curriculum, @term)
19
+ assert result.is_a?(Hash)
20
+ assert_equal :curriculum, result[:type]
21
+ }
22
+ end
23
+ end
24
+
25
+ should "raise CurriculumDoesNotExistError on invalid curriculums" do
26
+ @invalid_curriculums.each do |curriculum|
27
+ assert_raises(RubyUW::Errors::CurriculumDoesNotExistError) {
28
+ RubyUW::Curriculum.find(curriculum, @term)
29
+ }
30
+ end
31
+ end
32
+ end
33
+
34
+ context "proper grabbing of courses" do
35
+ setup do
36
+ @valid_curriculums = {
37
+ "BIO A" => {
38
+ "BIO A 201" => 10943..10949
39
+ }
40
+ }
41
+ end
42
+
43
+ should "extract proper amount of courses from a curriculum" do
44
+ @valid_curriculums.each do |curriculum, courses|
45
+ result = RubyUW::Curriculum.find(curriculum, @term)
46
+ assert result.is_a?(Hash)
47
+
48
+ courses.each do |expected_course, sln_range|
49
+ found = false
50
+ slns = []
51
+
52
+ result[:courses].each do |course, data|
53
+ next unless course[:name] == expected_course
54
+
55
+ data.each do |listing|
56
+ slns.push(listing[:sln].to_i)
57
+ end
58
+
59
+ found = true
60
+ break
61
+ end
62
+
63
+ assert found
64
+ assert !slns.empty?
65
+
66
+ sln_range.each { |sln|
67
+ assert slns.include?(sln)
68
+ slns -= [sln]
69
+ }
70
+ assert slns.empty?
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ context "live course listing grabbing" do
78
+ context "checking parsing of courses" do
79
+ setup do
80
+ @valid_courses = ["Chemistry 142", "CHEM 142", "CHEM 152", " BIO A 466"]
81
+ @invalid_courses = [" Chemistry 111 ", " CHEM 269"]
82
+ end
83
+
84
+ should "not raise any errors on valid courses" do
85
+ @valid_courses.each do |course|
86
+ assert_nothing_raised {
87
+ result = RubyUW::Curriculum.find(course, @term)
88
+ assert result.is_a?(Hash)
89
+ assert_equal :course, result[:type]
90
+ }
91
+ end
92
+ end
93
+
94
+ should "raise a CourseDoesNotExistError on invalid courses" do
95
+ @invalid_courses.each do |course|
96
+ assert_raises(RubyUW::Errors::CourseDoesNotExistError) {
97
+ RubyUW::Curriculum.find(course, @term)
98
+ }
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,256 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class CurriculumTest < Test::Unit::TestCase
4
+ setup do
5
+ RubyUW::Curriculum.publicize_singleton_methods
6
+ end
7
+
8
+ teardown do
9
+ RubyUW::Curriculum.protect_singleton_methods
10
+ end
11
+
12
+ context "properly routing to find_curriculum or find_course" do
13
+ setup do
14
+ @curriculums = ["Chemistry", "BIO A", "CSE", " CHEM ", "CHEMISTRY"]
15
+ @courses = ["CHEM 142", "BIO A 240", " CHEM 142 ", " BIO A 240 "]
16
+ @term = "WIN2010"
17
+ end
18
+
19
+ should "recognize each of the curriculums" do
20
+ RubyUW::Curriculum.expects(:find_curriculum).times(@curriculums.length)
21
+ RubyUW::Curriculum.expects(:find_course).never
22
+
23
+ @curriculums.each do |curriculum|
24
+ RubyUW::Curriculum.find(curriculum, @term)
25
+ end
26
+ end
27
+
28
+ should "recognize each of the courses" do
29
+ RubyUW::Curriculum.expects(:find_curriculum).never
30
+ RubyUW::Curriculum.expects(:find_course).times(@courses.length)
31
+
32
+ @courses.each do |course|
33
+ RubyUW::Curriculum.find(course, @term)
34
+ end
35
+ end
36
+ end
37
+
38
+ context "finding course information" do
39
+ setup do
40
+ @term = "WIN2010"
41
+ end
42
+
43
+ context "parsing course string" do
44
+ should "parse the proper curriculum from the course string" do
45
+ curriculums = {
46
+ "Chemistry 142" => ["Chemistry", "142"],
47
+ "CHEM 142" => ["CHEM", "142"],
48
+ "Chemistry 142" => ["Chemistry", "142"],
49
+ "BIO A 240" => ["BIO A", "240"],
50
+ "I'm invalid =]" => [nil, nil],
51
+ "CHEM INVALID" => [nil, nil]
52
+ }
53
+
54
+ curriculums.each do |course, curric|
55
+ parsed = RubyUW::Curriculum.parse_curriculum(course)
56
+ parsed_curric = parsed[0] unless parsed.nil?
57
+ parsed_number = parsed.nil? ? nil : parsed[1]
58
+ assert_equal curric[0], parsed_curric
59
+ assert_equal curric[1], parsed_number
60
+ end
61
+ end
62
+ end
63
+
64
+ context "finding course within returned curriculum information" do
65
+ setup do
66
+ @page = RubyUW::Base.connection.get(path_to_html("time_schedule_chem"))
67
+ @data = {
68
+ :type => :curriculum,
69
+ :courses => RubyUW::Curriculum.extract_courses(@page)
70
+ }
71
+
72
+ # Below pulled directly from Chem WIN2010 time schedule
73
+ @listings_count = {
74
+ "299" => 2,
75
+ "321" => 3,
76
+ "520" => 1,
77
+ "317" => 8
78
+ }
79
+
80
+ @nonexistent_course = "111"
81
+ end
82
+
83
+ should "return proper number of listings for a course" do
84
+ @listings_count.each do |course, amount|
85
+ listing = RubyUW::Curriculum.find_course_in_data(course, @data)
86
+
87
+ assert_not_nil listing
88
+ assert_equal amount, listing.length
89
+ end
90
+ end
91
+
92
+ should "return nil for a course which doesn't exist" do
93
+ assert_nil RubyUW::Curriculum.find_course_in_data(@nonexistent_course, @data)
94
+ end
95
+ end
96
+
97
+ context "the actual find_course method" do
98
+ setup do
99
+ @raw_curriculum = "CHEM 142"
100
+ @curriculum, @course = RubyUW::Curriculum.parse_curriculum(@raw_curriculum)
101
+ @page = RubyUW::Base.connection.get(path_to_html("time_schedule_chem"))
102
+ @data = {
103
+ :type => :curriculum,
104
+ :courses => RubyUW::Curriculum.extract_courses(@page)
105
+ }
106
+
107
+ @listing = RubyUW::Curriculum.find_course_in_data(@course, @data)
108
+
109
+ RubyUW::Curriculum.expects(:parse_curriculum).once.returns([@curriculum, @course])
110
+ RubyUW::Curriculum.expects(:find_curriculum).once.returns(@data)
111
+ end
112
+
113
+ should "raise a CourseDoesNotExistError if it can't be parsed" do
114
+ RubyUW::Curriculum.expects(:find_course_in_data).once.returns(nil)
115
+ assert_raises(RubyUW::Errors::CourseDoesNotExistError) {
116
+ RubyUW::Curriculum.find_course(@raw_curriculum, @term)
117
+ }
118
+ end
119
+
120
+ should "return a hash on a valid course" do
121
+ RubyUW::Curriculum.expects(:find_course_in_data).once.returns(@listing)
122
+ assert_nothing_raised {
123
+ returned = RubyUW::Curriculum.find_course(@raw_curriculum, @term)
124
+ assert returned.is_a?(Hash)
125
+ assert_equal :course, returned[:type]
126
+ assert returned[:courses].is_a?(Array)
127
+ }
128
+ end
129
+ end
130
+ end
131
+
132
+ context "finding curriculum information" do
133
+ context "with checking existence of a quarter" do
134
+ setup do
135
+ @page = mock('page')
136
+ end
137
+
138
+ should "return false if page contains the string 'Time Schedule - No Courses Offered'" do
139
+ @page.stubs(:body).returns("Time Schedule - No Courses Offered")
140
+ assert !RubyUW::Curriculum.quarter_exists?(@page)
141
+ end
142
+
143
+ should "return true if page does not contain that string" do
144
+ @page.stubs(:body).returns("HI!")
145
+ assert RubyUW::Curriculum.quarter_exists?(@page)
146
+ end
147
+ end
148
+
149
+ context "with checking existence of a curriculum" do
150
+ setup do
151
+ @page = mock('page')
152
+ end
153
+
154
+ should "return false if page contains the string 'Time Schedule - No Courses Offered'" do
155
+ @page.stubs(:body).returns("Time Schedule - No Courses Offered")
156
+ assert !RubyUW::Curriculum.curriculum_exists?(@page)
157
+ end
158
+
159
+ should "return true if page does not contain that string" do
160
+ @page.stubs(:body).returns("HI!")
161
+ assert RubyUW::Curriculum.curriculum_exists?(@page)
162
+ end
163
+ end
164
+
165
+ context "with finding the link for a curriculum" do
166
+ setup do
167
+ @page = RubyUW::Base.connection.get(path_to_html("quarter_time_schedule"))
168
+
169
+ @curriculums = {
170
+ "Chemistry" => "chem.html",
171
+ "Biology" => "biology.html",
172
+ "JAPAN" => "japanese.html",
173
+ "BIO A" => "bioanth.html",
174
+ "Underwater Basket" => nil,
175
+ "UBASK" => nil
176
+ }
177
+ end
178
+
179
+ should "extract proper URLs for curriculums" do
180
+ @curriculums.each do |curric, url|
181
+ assert_equal url, RubyUW::Curriculum.extract_curriculum_url(curric, @page)
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ context "extracting course information" do
188
+ setup do
189
+ @page = RubyUW::Base.connection.get(path_to_html("time_schedule_chem"))
190
+ end
191
+
192
+ context "extracting course headers" do
193
+ setup do
194
+ @header = {
195
+ :name => "CHEM 142",
196
+ :title => "GENERAL CHEMISTRY"
197
+ }
198
+ end
199
+
200
+ should "extract proper information out of a header" do
201
+ header_node = @page.search("//table[@bgcolor=\"#99ccff\"]")[0]
202
+
203
+ returned_header = RubyUW::Curriculum.extract_course_header(header_node)
204
+ @header.each do |k,v|
205
+ assert_equal v, returned_header[k]
206
+ end
207
+ end
208
+
209
+ should "return nil for an invalid header" do
210
+ header_node = @page.search("//table[@bgcolor=\"#d3d3d3\"]")[0]
211
+
212
+ assert_nothing_raised {
213
+ assert_nil RubyUW::Curriculum.extract_course_header(header_node)
214
+ }
215
+ end
216
+ end
217
+
218
+ context "extracting a course listing" do
219
+ setup do
220
+ @listing = {
221
+ :sln => "11610",
222
+ :section => "A",
223
+ :type => "LC"
224
+ }
225
+ end
226
+
227
+ should "return the proper information for the first listing" do
228
+ node = @page.search("//table[@bgcolor=\"#99ccff\"]//following-sibling::table")[0]
229
+
230
+ returned = RubyUW::Curriculum.extract_course_listing(node)
231
+ @listing.each do |k,v|
232
+ assert_equal v, returned[k]
233
+ end
234
+ end
235
+
236
+ should "raise an exception if the listing does not match" do
237
+ pre_node = mock('pre_node')
238
+ pre_node.expects(:inner_text).once.returns("I don't match")
239
+ node = mock('node')
240
+ node.expects(:search).once.returns(pre_node)
241
+
242
+ assert_raises(RubyUW::Errors::CourseListingMatchError) {
243
+ RubyUW::Curriculum.extract_course_listing(node)
244
+ }
245
+ end
246
+ end
247
+
248
+ context "extracting the list of courses" do
249
+ should "not raise any errors on a valid page" do
250
+ assert_nothing_raised {
251
+ RubyUW::Curriculum.extract_courses(@page)
252
+ }
253
+ end
254
+ end
255
+ end
256
+ end