netzke-basepack 0.7.4 → 0.7.5
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/.travis.yml +11 -0
- data/CHANGELOG.rdoc +10 -0
- data/README.md +36 -2
- data/Rakefile +1 -3
- data/config/ci/before-travis.sh +28 -0
- data/lib/netzke/active_record.rb +10 -8
- data/lib/netzke/active_record/attributes.rb +28 -17
- data/lib/netzke/active_record/relation_extensions.rb +3 -1
- data/lib/netzke/basepack.rb +10 -2
- data/lib/netzke/basepack/action_column.rb +6 -8
- data/lib/netzke/basepack/data_accessor.rb +11 -174
- data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +164 -0
- data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +279 -0
- data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +264 -0
- data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +260 -0
- data/lib/netzke/basepack/form_panel.rb +3 -3
- data/lib/netzke/basepack/form_panel/fields.rb +6 -10
- data/lib/netzke/basepack/form_panel/javascripts/form_panel.js +1 -0
- data/lib/netzke/basepack/form_panel/services.rb +15 -16
- data/lib/netzke/basepack/grid_panel.rb +16 -10
- data/lib/netzke/basepack/grid_panel/columns.rb +6 -7
- data/lib/netzke/basepack/grid_panel/javascripts/event_handling.js +29 -27
- data/lib/netzke/basepack/grid_panel/services.rb +13 -90
- data/lib/netzke/basepack/paging_form_panel.rb +3 -3
- data/lib/netzke/basepack/query_builder.rb +2 -0
- data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +29 -19
- data/lib/netzke/basepack/search_panel.rb +6 -3
- data/lib/netzke/basepack/search_panel/javascripts/search_panel.js +2 -1
- data/lib/netzke/basepack/search_window.rb +2 -1
- data/lib/netzke/basepack/version.rb +1 -1
- data/lib/netzke/data_mapper.rb +18 -0
- data/lib/netzke/data_mapper/attributes.rb +273 -0
- data/lib/netzke/data_mapper/combobox_options.rb +11 -0
- data/lib/netzke/data_mapper/relation_extensions.rb +38 -0
- data/lib/netzke/sequel.rb +18 -0
- data/lib/netzke/sequel/attributes.rb +274 -0
- data/lib/netzke/sequel/combobox_options.rb +10 -0
- data/lib/netzke/sequel/relation_extensions.rb +40 -0
- data/netzke-basepack.gemspec +24 -13
- data/test/basepack_test_app/Gemfile +33 -8
- data/test/basepack_test_app/Gemfile.lock +98 -79
- data/test/basepack_test_app/Guardfile +46 -0
- data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +3 -0
- data/test/basepack_test_app/app/components/extras/book_presentation.rb +10 -3
- data/test/basepack_test_app/app/models/address.rb +27 -1
- data/test/basepack_test_app/app/models/author.rb +28 -0
- data/test/basepack_test_app/app/models/book.rb +43 -0
- data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +22 -0
- data/test/basepack_test_app/app/models/role.rb +21 -0
- data/test/basepack_test_app/app/models/user.rb +24 -0
- data/test/basepack_test_app/config/database.yml.sample +11 -10
- data/test/basepack_test_app/config/database.yml.travis +15 -0
- data/test/basepack_test_app/config/initializers/data_mapper_logging.rb +3 -0
- data/test/basepack_test_app/config/initializers/sequel.rb +26 -0
- data/test/basepack_test_app/db/schema.rb +0 -3
- data/test/basepack_test_app/features/grid_panel.feature +28 -8
- data/test/basepack_test_app/features/grid_sorting.feature +6 -6
- data/test/basepack_test_app/features/paging_form_panel.feature +13 -13
- data/test/basepack_test_app/features/search_in_grid.feature +31 -31
- data/test/basepack_test_app/features/step_definitions/generic_steps.rb +3 -1
- data/test/basepack_test_app/features/support/env.rb +17 -4
- data/test/basepack_test_app/lib/tasks/travis.rake +7 -0
- data/test/basepack_test_app/spec/components/form_panel_spec.rb +2 -2
- data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +68 -0
- data/test/basepack_test_app/spec/{active_record → data_adapter}/attributes_spec.rb +12 -4
- data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +125 -0
- data/test/basepack_test_app/spec/spec_helper.rb +9 -0
- data/test/unit/active_record_basepack_test.rb +1 -1
- data/test/unit/grid_panel_test.rb +1 -1
- metadata +26 -31
- data/app/models/netzke_field_list.rb +0 -261
- data/app/models/netzke_model_attr_list.rb +0 -21
- data/app/models/netzke_persistent_array_auto_model.rb +0 -57
- data/test/basepack_test_app/spec/active_record/relation_extensions_spec.rb +0 -44
@@ -0,0 +1,46 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch('Gemfile')
|
6
|
+
# Uncomment next line if Gemfile contain `gemspec' command
|
7
|
+
# watch(/^.+\.gemspec/)
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'rails' do
|
11
|
+
watch('Gemfile.lock')
|
12
|
+
watch(%r{^(config|lib)/.*})
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
guard 'livereload' do
|
17
|
+
watch(%r{app/.+\.(erb|haml)})
|
18
|
+
watch(%r{app/helpers/.+\.rb})
|
19
|
+
watch(%r{(public/|app/assets).+\.(css|js|html)})
|
20
|
+
watch(%r{(app/assets/.+\.css)\.s[ac]ss}) { |m| m[1] }
|
21
|
+
watch(%r{(app/assets/.+\.js)\.coffee}) { |m| m[1] }
|
22
|
+
watch(%r{config/locales/.+\.yml})
|
23
|
+
end
|
24
|
+
|
25
|
+
guard 'rspec', :version => 2, :cli => '--color' do
|
26
|
+
watch(%r{^spec/.+_spec\.rb$})
|
27
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
28
|
+
watch('spec/spec_helper.rb') { "spec" }
|
29
|
+
|
30
|
+
# Rails example
|
31
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
32
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
33
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
34
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
35
|
+
watch('config/routes.rb') { "spec/routing" }
|
36
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
37
|
+
# Capybara request specs
|
38
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
guard 'cucumber' do
|
43
|
+
watch(%r{^features/.+\.feature$})
|
44
|
+
watch(%r{^features/support/.+$}) { 'features' }
|
45
|
+
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
|
46
|
+
end
|
@@ -3,10 +3,17 @@ module Extras
|
|
3
3
|
# A setter that creates an author on the fly
|
4
4
|
def author_first_name_setter
|
5
5
|
lambda do |r,v|
|
6
|
+
|
7
|
+
data_adapter = Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(Author).new(Author)
|
8
|
+
# cast v to integer, if possible
|
9
|
+
v = v.to_i if v.kind_of?(String) && v.match(/[[:digit:]]+/)
|
6
10
|
if v.is_a?(Integer)
|
7
|
-
r.author =
|
11
|
+
r.author = data_adapter.find_record(v)
|
8
12
|
else
|
9
|
-
|
13
|
+
author = data_adapter.new_record(:first_name => v)
|
14
|
+
# Sequel doesn't know of save!
|
15
|
+
author.respond_to?(:save!) ? author.save! : author.save(:raise_on_save_failure => true)
|
16
|
+
r.author = author
|
10
17
|
end
|
11
18
|
end
|
12
19
|
end
|
@@ -17,4 +24,4 @@ module Extras
|
|
17
24
|
end
|
18
25
|
|
19
26
|
end
|
20
|
-
end
|
27
|
+
end
|
@@ -1,3 +1,29 @@
|
|
1
|
+
if defined? DataMapper::Resource
|
2
|
+
|
3
|
+
class Address
|
4
|
+
include DataMapper::Resource
|
5
|
+
property :id, Serial
|
6
|
+
belongs_to :user
|
7
|
+
property :street, String
|
8
|
+
property :city, String
|
9
|
+
property :postcode, String
|
10
|
+
property :created_at, DateTime
|
11
|
+
property :updated_at, DateTime
|
12
|
+
end
|
13
|
+
|
14
|
+
elsif defined? Sequel::Model
|
15
|
+
|
16
|
+
class Address < Sequel::Model
|
17
|
+
# although this is one_to_one, according to Sequel docs,
|
18
|
+
# the model containing the foreign key should have many_to_one
|
19
|
+
# and the other model should have one_to_one
|
20
|
+
many_to_one :user
|
21
|
+
end
|
22
|
+
|
23
|
+
else
|
24
|
+
|
1
25
|
class Address < ActiveRecord::Base
|
2
26
|
belongs_to :user
|
3
|
-
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -1,5 +1,32 @@
|
|
1
|
+
if defined? DataMapper::Resource
|
2
|
+
|
3
|
+
class Author
|
4
|
+
include DataMapper::Resource
|
5
|
+
property :id, Serial
|
6
|
+
property :first_name, String
|
7
|
+
property :last_name, String
|
8
|
+
property :created_at, DateTime
|
9
|
+
property :updated_at, DateTime
|
10
|
+
has n, :books
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
elsif defined? Sequel::Model
|
15
|
+
|
16
|
+
class Author < Sequel::Model
|
17
|
+
one_to_many :books
|
18
|
+
end
|
19
|
+
|
20
|
+
else
|
21
|
+
|
1
22
|
class Author < ActiveRecord::Base
|
2
23
|
has_many :books
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# ORM-agnostic bits
|
29
|
+
class Author
|
3
30
|
|
4
31
|
# virtual attribute
|
5
32
|
def name
|
@@ -7,4 +34,5 @@ class Author < ActiveRecord::Base
|
|
7
34
|
end
|
8
35
|
|
9
36
|
netzke_attribute :name
|
37
|
+
|
10
38
|
end
|
@@ -1,6 +1,49 @@
|
|
1
|
+
if defined? DataMapper::Resource
|
2
|
+
|
3
|
+
class Book
|
4
|
+
include DataMapper::Resource
|
5
|
+
property :id, Serial
|
6
|
+
belongs_to :author, :required => false
|
7
|
+
validates_presence_of :title, :message => "Title can't be blank"
|
8
|
+
property :title, String
|
9
|
+
property :exemplars, Integer
|
10
|
+
property :digitized, Boolean
|
11
|
+
property :notes, Text
|
12
|
+
property :tags, String
|
13
|
+
property :rating, Integer
|
14
|
+
property :created_at, DateTime
|
15
|
+
property :updated_at, DateTime
|
16
|
+
property :last_read_at, DateTime
|
17
|
+
property :published_on, Date
|
18
|
+
|
19
|
+
def self.sorted_by_author_name dir
|
20
|
+
all :order => [ author.last_name.send(dir), author.first_name.send(dir) ], :links => [ relationships[:author].inverse ]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
elsif defined? Sequel::Model
|
26
|
+
|
27
|
+
class Book < Sequel::Model
|
28
|
+
many_to_one :author
|
29
|
+
|
30
|
+
def_dataset_method(:sorted_by_author_name) do |dir|
|
31
|
+
eager_graph(:author).order_append(:author__last_name.send(dir), :author__first_name.send(dir))
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate
|
35
|
+
validates_presence :title, :message => "can't be blank"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
else
|
41
|
+
|
1
42
|
class Book < ActiveRecord::Base
|
2
43
|
belongs_to :author
|
3
44
|
validates_presence_of :title
|
4
45
|
|
5
46
|
scope :sorted_by_author_name, lambda { |dir| joins(:author).order("authors.last_name #{dir}, authors.first_name #{dir}") }
|
6
47
|
end
|
48
|
+
|
49
|
+
end
|
@@ -1,4 +1,26 @@
|
|
1
|
+
if defined? DataMapper::Resource
|
2
|
+
|
3
|
+
class BookWithCustomPrimaryKey
|
4
|
+
include DataMapper::Resource
|
5
|
+
property :uid, Serial
|
6
|
+
belongs_to :author
|
7
|
+
property :title, String
|
8
|
+
property :created_at, DateTime
|
9
|
+
property :updated_at, DateTime
|
10
|
+
end
|
11
|
+
|
12
|
+
elsif defined? Sequel::Model
|
13
|
+
|
14
|
+
class BookWithCustomPrimaryKey < Sequel::Model
|
15
|
+
set_primary_key :uid
|
16
|
+
many_to_one :author
|
17
|
+
end
|
18
|
+
|
19
|
+
else
|
20
|
+
|
1
21
|
class BookWithCustomPrimaryKey < ActiveRecord::Base
|
2
22
|
set_primary_key 'uid'
|
3
23
|
belongs_to :author
|
4
24
|
end
|
25
|
+
|
26
|
+
end
|
@@ -1,3 +1,24 @@
|
|
1
|
+
if defined? DataMapper::Resource
|
2
|
+
|
3
|
+
class Role
|
4
|
+
include DataMapper::Resource
|
5
|
+
property :id, Serial
|
6
|
+
property :name, String
|
7
|
+
has n, :users
|
8
|
+
property :created_at, DateTime
|
9
|
+
property :updated_at, DateTime
|
10
|
+
end
|
11
|
+
|
12
|
+
elsif defined? Sequel::Model
|
13
|
+
|
14
|
+
class Role < Sequel::Model
|
15
|
+
one_to_many :users
|
16
|
+
end
|
17
|
+
|
18
|
+
else
|
19
|
+
|
1
20
|
class Role < ActiveRecord::Base
|
2
21
|
has_many :users
|
3
22
|
end
|
23
|
+
|
24
|
+
end
|
@@ -1,5 +1,29 @@
|
|
1
|
+
if defined? DataMapper::Resource
|
2
|
+
|
3
|
+
class User
|
4
|
+
include DataMapper::Resource
|
5
|
+
property :id, Serial
|
6
|
+
property :first_name, String
|
7
|
+
property :last_name, String
|
8
|
+
belongs_to :role, :required => false
|
9
|
+
has 1, :address
|
10
|
+
property :created_at, DateTime
|
11
|
+
property :updated_at, DateTime
|
12
|
+
end
|
13
|
+
|
14
|
+
elsif defined? Sequel::Model
|
15
|
+
|
16
|
+
class User < Sequel::Model
|
17
|
+
many_to_one :role
|
18
|
+
one_to_one :address
|
19
|
+
end
|
20
|
+
|
21
|
+
else
|
22
|
+
|
1
23
|
class User < ActiveRecord::Base
|
2
24
|
# scope :latest, lambda {|param| where(:created_at.gt => param)}
|
3
25
|
belongs_to :role
|
4
26
|
has_one :address
|
5
27
|
end
|
28
|
+
|
29
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
development:
|
1
|
+
development: &development
|
2
2
|
adapter: mysql2
|
3
3
|
encoding: utf8
|
4
4
|
reconnect: false
|
@@ -6,16 +6,13 @@ development:
|
|
6
6
|
pool: 5
|
7
7
|
username: root
|
8
8
|
password:
|
9
|
-
socket: /tmp/mysql.sock
|
9
|
+
socket: <%= ["/opt/local/var/run/mysql5/mysqld.sock", "/tmp/mysqld.sock", "/tmp/mysql.sock", "/var/run/mysqld/mysqld.sock", "/var/lib/mysql/mysql.sock"].detect{ |socket| File.exist?(socket) } %>
|
10
|
+
# DM
|
11
|
+
uri: mysql://localhost/nbt_development?user=root&encoding=UTF-8
|
10
12
|
|
11
13
|
# Warning: The database defined as "test" will be erased and
|
12
14
|
# re-generated from your development database when you run "rake".
|
13
15
|
# Do not set this db to the same as development or production.
|
14
|
-
# test:
|
15
|
-
# adapter: sqlite3
|
16
|
-
# database: db/test.sqlite3
|
17
|
-
# pool: 5
|
18
|
-
# timeout: 5000
|
19
16
|
test: &test
|
20
17
|
adapter: mysql2
|
21
18
|
encoding: utf8
|
@@ -24,7 +21,9 @@ test: &test
|
|
24
21
|
pool: 5
|
25
22
|
username: root
|
26
23
|
password:
|
27
|
-
socket: /tmp/mysql.sock
|
24
|
+
socket: <%= ["/opt/local/var/run/mysql5/mysqld.sock", "/tmp/mysqld.sock", "/tmp/mysql.sock", "/var/run/mysqld/mysqld.sock", "/var/lib/mysql/mysql.sock"].detect{ |socket| File.exist?(socket) } %>
|
25
|
+
# DM
|
26
|
+
uri: mysql://localhost/nbt_test?user=root&encoding=UTF-8
|
28
27
|
|
29
28
|
production:
|
30
29
|
adapter: mysql2
|
@@ -34,7 +33,9 @@ production:
|
|
34
33
|
pool: 5
|
35
34
|
username: root
|
36
35
|
password:
|
37
|
-
socket: /tmp/mysql.sock
|
36
|
+
socket: <%= ["/opt/local/var/run/mysql5/mysqld.sock", "/tmp/mysqld.sock", "/tmp/mysql.sock", "/var/run/mysqld/mysqld.sock", "/var/lib/mysql/mysql.sock"].detect{ |socket| File.exist?(socket) } %>
|
37
|
+
# DM
|
38
|
+
uri: mysql://localhost/nbt_production?user=root&encoding=UTF-8
|
38
39
|
|
39
40
|
cucumber:
|
40
|
-
<<: *test
|
41
|
+
<<: *test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Warning: The database defined as "test" will be erased and
|
2
|
+
# re-generated from your development database when you run "rake".
|
3
|
+
# Do not set this db to the same as development or production.
|
4
|
+
test: &test
|
5
|
+
adapter: mysql2
|
6
|
+
encoding: utf8
|
7
|
+
reconnect: false
|
8
|
+
database: nbt_test
|
9
|
+
pool: 5
|
10
|
+
username:
|
11
|
+
# DM
|
12
|
+
uri: mysql://127.0.0.1/nbt_test?user=&encoding=UTF-8
|
13
|
+
|
14
|
+
cucumber:
|
15
|
+
<<: *test
|
@@ -0,0 +1,26 @@
|
|
1
|
+
if defined? Sequel
|
2
|
+
Sequel::Model.plugin :active_model
|
3
|
+
Sequel::Model.plugin :validation_helpers
|
4
|
+
db = Sequel.connect(YAML.load(ERB.new(File.read(File.join(Rails.root,'config','database.yml'))).result)[Rails.env])
|
5
|
+
db.logger = Logger.new $stdout if Rails.env.development?
|
6
|
+
|
7
|
+
Sequel::Model.class_eval do
|
8
|
+
# Emulate ARs timestamp behavior
|
9
|
+
def before_create
|
10
|
+
self.created_at ||= Time.now
|
11
|
+
self.updated_at ||= Time.now
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_update
|
15
|
+
self.updated_at ||= Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
# enable mass-assignment of pk, so that pickle scenarios can work properly when id is specified
|
19
|
+
unrestrict_primary_key
|
20
|
+
|
21
|
+
# FactoryGirl compatibility fix
|
22
|
+
def save!
|
23
|
+
save :raise_on_save_failure => true, :validate => false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -64,9 +64,6 @@ ActiveRecord::Schema.define(:version => 20110909071740) do
|
|
64
64
|
add_index "netzke_component_states", ["role_id"], :name => "index_netzke_component_states_on_role_id"
|
65
65
|
add_index "netzke_component_states", ["user_id"], :name => "index_netzke_component_states_on_user_id"
|
66
66
|
|
67
|
-
create_table "netzke_temp_table", :force => true do |t|
|
68
|
-
end
|
69
|
-
|
70
67
|
create_table "roles", :force => true do |t|
|
71
68
|
t.string "name"
|
72
69
|
t.datetime "created_at"
|
@@ -212,8 +212,9 @@ Scenario: Advanced search window should be hidable after loading grid panel dyna
|
|
212
212
|
Scenario: Column order should be saved across page reloads
|
213
213
|
Given I am on the BookGridWithPersistence test page
|
214
214
|
When I drag "Digitized" column before "Title"
|
215
|
+
And I wait for the response from the server
|
215
216
|
And I go to the BookGridWithPersistence test page
|
216
|
-
Then I should see columns in order: "
|
217
|
+
Then I should see columns in order: "Digitized", "Title", "Exemplars"
|
217
218
|
|
218
219
|
@javascript
|
219
220
|
Scenario: I must see total records value
|
@@ -248,10 +249,29 @@ Scenario: GridPanel with overridden columns
|
|
248
249
|
Then the grid's column "In abundance" should not be editable
|
249
250
|
|
250
251
|
@javascript
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
252
|
+
Scenario: Delete record via an column action
|
253
|
+
Given a book exists with title: "Some Title"
|
254
|
+
When I go to the BookGridWithColumnActions test page
|
255
|
+
And I click the "Delete row" action icon
|
256
|
+
And I press "Yes"
|
257
|
+
Then I should see "Deleted 1 record(s)"
|
258
|
+
And a book should not exist with title: "Some Title"
|
259
|
+
|
260
|
+
@javascript
|
261
|
+
Scenario: Pagination in grid panel
|
262
|
+
Given the following books exist:
|
263
|
+
| title |
|
264
|
+
| Journey to Ixtlan |
|
265
|
+
| Lolita |
|
266
|
+
| Getting Things Done |
|
267
|
+
| Magus |
|
268
|
+
When I go to the BookGridWithPaging test page
|
269
|
+
Then I should see "Journey to Ixtlan"
|
270
|
+
And I should see "Lolita"
|
271
|
+
But I should not see "Getting Things Done"
|
272
|
+
And the grid should show 2 records
|
273
|
+
|
274
|
+
When I go forward one page
|
275
|
+
And I wait for the response from the server
|
276
|
+
Then I should see "Getting Things Done"
|
277
|
+
And I should see "Magus"
|