rubyuw 0.99.2 → 0.99.3

Sign up to get free protection for your applications and to get access to all the features.
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