paper_trail_manager 0.6.0 → 0.8.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +54 -0
- data/.gitignore +3 -2
- data/.rubocop.yml +33 -0
- data/.ruby-version +1 -0
- data/Appraisals +15 -12
- data/CHANGES.md +24 -0
- data/Gemfile +2 -1
- data/README.md +130 -66
- data/Rakefile +19 -5
- data/app/controllers/paper_trail_manager/changes_controller.rb +119 -101
- data/app/helpers/paper_trail_manager/changes_helper.rb +17 -13
- data/app/views/paper_trail_manager/changes/_version.html.erb +1 -1
- data/app/views/paper_trail_manager/changes/index.atom.builder +23 -19
- data/app/views/paper_trail_manager/changes/index.html.erb +41 -28
- data/app/views/paper_trail_manager/changes/show.html.erb +9 -6
- data/gemfiles/rails_6.1_paper_trail_12.0_kaminari.gemfile +10 -0
- data/gemfiles/rails_6.1_paper_trail_12.0_will_paginate.gemfile +10 -0
- data/gemfiles/rails_7.0_paper_trail_12.0_kaminari.gemfile +10 -0
- data/gemfiles/rails_7.0_paper_trail_12.0_will_paginate.gemfile +10 -0
- data/gemfiles/rails_7.0_paper_trail_15.0_kaminari.gemfile +10 -0
- data/gemfiles/rails_7.0_paper_trail_15.0_will_paginate.gemfile +10 -0
- data/gemfiles/rails_7.1_paper_trail_15.0_kaminari.gemfile +10 -0
- data/gemfiles/rails_7.1_paper_trail_15.0_will_paginate.gemfile +10 -0
- data/lib/paper_trail_manager.rb +11 -9
- data/paper_trail_manager.gemspec +25 -24
- data/spec/app_template.rb +39 -0
- data/spec/integration/authorization_spec.rb +84 -0
- data/spec/integration/date_filter_spec.rb +84 -0
- data/spec/integration/navigation_spec.rb +5 -3
- data/spec/integration/paper_trail_manager_spec.rb +94 -110
- data/spec/integration/response_formats_spec.rb +73 -0
- data/spec/rails_helper.rb +6 -4
- data/spec/spec_helper.rb +7 -5
- data/spec/support/factories.rb +4 -3
- data/spec/support/rspec_html_matchers.rb +7 -0
- data/spec/unit/authorization_spec.rb +42 -0
- data/spec/unit/changes_helper_spec.rb +81 -0
- metadata +103 -238
- data/.travis.yml +0 -23
- data/gemfiles/rails_3.2.0_paper_trail_3.0_kaminari.gemfile +0 -10
- data/gemfiles/rails_3.2.0_paper_trail_3.0_will_paginate.gemfile +0 -10
- data/gemfiles/rails_3.2.0_paper_trail_4.0_kaminari.gemfile +0 -10
- data/gemfiles/rails_3.2.0_paper_trail_4.0_will_paginate.gemfile +0 -10
- data/gemfiles/rails_4.0.0_paper_trail_3.0_kaminari.gemfile +0 -9
- data/gemfiles/rails_4.0.0_paper_trail_3.0_will_paginate.gemfile +0 -9
- data/gemfiles/rails_4.0.0_paper_trail_4.0_kaminari.gemfile +0 -9
- data/gemfiles/rails_4.0.0_paper_trail_4.0_will_paginate.gemfile +0 -9
- data/gemfiles/rails_4.1.0_paper_trail_3.0_kaminari.gemfile +0 -9
- data/gemfiles/rails_4.1.0_paper_trail_3.0_will_paginate.gemfile +0 -9
- data/gemfiles/rails_4.1.0_paper_trail_4.0_kaminari.gemfile +0 -9
- data/gemfiles/rails_4.1.0_paper_trail_4.0_will_paginate.gemfile +0 -9
- data/gemfiles/rails_4.2.2_paper_trail_3.0_kaminari.gemfile +0 -9
- data/gemfiles/rails_4.2.2_paper_trail_3.0_will_paginate.gemfile +0 -9
- data/gemfiles/rails_4.2.2_paper_trail_4.0_kaminari.gemfile +0 -9
- data/gemfiles/rails_4.2.2_paper_trail_4.0_will_paginate.gemfile +0 -9
- data/spec/controllers/entities_controller_spec.rb +0 -125
- data/spec/controllers/platforms_controller_spec.rb +0 -125
- data/spec/dummy/.gitignore +0 -15
- data/spec/dummy/Gemfile +0 -9
- data/spec/dummy/README.rdoc +0 -261
- data/spec/dummy/Rakefile +0 -7
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +0 -15
- data/spec/dummy/app/assets/stylesheets/application.css +0 -13
- data/spec/dummy/app/controllers/application_controller.rb +0 -6
- data/spec/dummy/app/controllers/entities_controller.rb +0 -83
- data/spec/dummy/app/controllers/platforms_controller.rb +0 -83
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/helpers/entities_helper.rb +0 -2
- data/spec/dummy/app/helpers/platforms_helper.rb +0 -2
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/entity.rb +0 -6
- data/spec/dummy/app/models/platform.rb +0 -6
- data/spec/dummy/app/views/application/index.html.erb +0 -6
- data/spec/dummy/app/views/entities/_form.html.erb +0 -17
- data/spec/dummy/app/views/entities/edit.html.erb +0 -6
- data/spec/dummy/app/views/entities/index.html.erb +0 -21
- data/spec/dummy/app/views/entities/new.html.erb +0 -5
- data/spec/dummy/app/views/entities/show.html.erb +0 -5
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/app/views/platforms/_form.html.erb +0 -17
- data/spec/dummy/app/views/platforms/edit.html.erb +0 -6
- data/spec/dummy/app/views/platforms/index.html.erb +0 -21
- data/spec/dummy/app/views/platforms/new.html.erb +0 -5
- data/spec/dummy/app/views/platforms/show.html.erb +0 -5
- data/spec/dummy/config/application.rb +0 -64
- data/spec/dummy/config/boot.rb +0 -6
- data/spec/dummy/config/database.yml +0 -22
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -37
- data/spec/dummy/config/environments/production.rb +0 -67
- data/spec/dummy/config/environments/test.rb +0 -36
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/inflections.rb +0 -15
- data/spec/dummy/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/config/initializers/session_store.rb +0 -8
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/config/locales/en.yml +0 -5
- data/spec/dummy/config/routes.rb +0 -8
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/db/migrate/20110228091428_create_entities.rb +0 -14
- data/spec/dummy/db/migrate/20110228093241_create_platforms.rb +0 -14
- data/spec/dummy/db/migrate/20110228094444_create_versions.rb +0 -18
- data/spec/dummy/db/schema.rb +0 -41
- data/spec/dummy/db/seeds.rb +0 -7
- data/spec/dummy/doc/README_FOR_APP +0 -2
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -25
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +0 -241
- data/spec/dummy/public/robots.txt +0 -5
- data/spec/dummy/script/rails +0 -6
- data/spec/dummy/test/fixtures/.gitkeep +0 -0
- data/spec/dummy/test/functional/.gitkeep +0 -0
- data/spec/dummy/test/integration/.gitkeep +0 -0
- data/spec/dummy/test/performance/browsing_test.rb +0 -12
- data/spec/dummy/test/test_helper.rb +0 -13
- data/spec/dummy/test/unit/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy/vendor/plugins/.gitkeep +0 -0
- data/spec/helpers/entities_helper_spec.rb +0 -15
- data/spec/helpers/platforms_helper_spec.rb +0 -15
- data/spec/models/entity_spec.rb +0 -14
- data/spec/models/platform_spec.rb +0 -14
- data/spec/requests/entities_spec.rb +0 -11
- data/spec/requests/platforms_spec.rb +0 -11
- data/spec/routing/entities_routing_spec.rb +0 -35
- data/spec/routing/platforms_routing_spec.rb +0 -35
- data/spec/views/entities/edit.html.erb_spec.rb +0 -15
- data/spec/views/entities/index.html.erb_spec.rb +0 -14
- data/spec/views/entities/new.html.erb_spec.rb +0 -15
- data/spec/views/entities/show.html.erb_spec.rb +0 -11
- data/spec/views/platforms/edit.html.erb_spec.rb +0 -15
- data/spec/views/platforms/index.html.erb_spec.rb +0 -14
- data/spec/views/platforms/new.html.erb_spec.rb +0 -15
- data/spec/views/platforms/show.html.erb_spec.rb +0 -11
data/paper_trail_manager.gemspec
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
5
|
|
|
5
6
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name =
|
|
7
|
-
spec.version =
|
|
8
|
-
spec.authors = [
|
|
9
|
-
spec.
|
|
10
|
-
spec.summary =
|
|
11
|
-
spec.description =
|
|
12
|
-
spec.homepage =
|
|
13
|
-
spec.license =
|
|
14
|
-
|
|
7
|
+
spec.name = 'paper_trail_manager'
|
|
8
|
+
spec.version = '0.8.0'
|
|
9
|
+
spec.authors = ['Igal Koshevoy', 'Reid Beels']
|
|
10
|
+
spec.email = ['mail@reidbeels.com']
|
|
11
|
+
spec.summary = 'A user interface for `paper_trail` versioning data in Rails applications.'
|
|
12
|
+
spec.description = 'Browse, subscribe, view and revert changes to records when using Rails and the `paper_trail` gem.'
|
|
13
|
+
spec.homepage = 'https://github.com/DamageLabs/paper_trail_manager'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
|
-
spec.require_paths = [
|
|
19
|
-
|
|
20
|
-
spec.add_dependency
|
|
21
|
-
spec.add_dependency
|
|
22
|
-
|
|
23
|
-
spec.
|
|
24
|
-
spec.add_development_dependency
|
|
25
|
-
spec.add_development_dependency
|
|
26
|
-
spec.add_development_dependency
|
|
27
|
-
spec.add_development_dependency
|
|
28
|
-
spec.add_development_dependency
|
|
29
|
-
spec.add_development_dependency
|
|
18
|
+
spec.require_paths = ['lib']
|
|
19
|
+
spec.required_ruby_version = '>= 3.1'
|
|
20
|
+
spec.add_dependency 'paper_trail', ['>= 12.0']
|
|
21
|
+
spec.add_dependency 'rails', ['>= 6.1', '< 8.0']
|
|
22
|
+
spec.add_dependency 'nokogiri', ['>= 1.18.3']
|
|
23
|
+
spec.add_dependency 'rails-html-sanitizer', ['>= 1.6.1']
|
|
24
|
+
spec.add_development_dependency 'appraisal', '~> 2.0'
|
|
25
|
+
spec.add_development_dependency 'factory_bot_rails', '~> 6.0'
|
|
26
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
|
27
|
+
spec.add_development_dependency 'rspec-activemodel-mocks', '~> 1.0'
|
|
28
|
+
spec.add_development_dependency 'rspec-html-matchers', '~> 0.10'
|
|
29
|
+
spec.add_development_dependency 'rspec-its', '~> 1.0'
|
|
30
|
+
spec.add_development_dependency 'rspec-rails', '~> 6.0'
|
|
31
|
+
spec.add_development_dependency 'sqlite3', '~> 1.7'
|
|
30
32
|
end
|
|
31
|
-
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
gem 'paper_trail_manager', path: __FILE__ + '/../../../'
|
|
4
|
+
|
|
5
|
+
# Remove auto-generated request specs from dummy app
|
|
6
|
+
remove_file 'spec/requests/entities_spec.rb' if File.exist?('spec/requests/entities_spec.rb')
|
|
7
|
+
remove_file 'spec/requests/platforms_spec.rb' if File.exist?('spec/requests/platforms_spec.rb')
|
|
8
|
+
|
|
9
|
+
generate 'paper_trail:install'
|
|
10
|
+
generate 'resource', 'entity name:string status:string --no-controller-specs --no-helper-specs'
|
|
11
|
+
generate 'resource', 'platform name:string status:string --no-controller-specs --no-helper-specs'
|
|
12
|
+
|
|
13
|
+
# Remove auto-generated spec files that conflict with our factories
|
|
14
|
+
remove_file 'spec/models/entity_spec.rb' if File.exist?('spec/models/entity_spec.rb')
|
|
15
|
+
remove_file 'spec/models/platform_spec.rb' if File.exist?('spec/models/platform_spec.rb')
|
|
16
|
+
|
|
17
|
+
model_body = <<-MODEL
|
|
18
|
+
has_paper_trail
|
|
19
|
+
|
|
20
|
+
validates :name, presence: true
|
|
21
|
+
validates :status, presence: true
|
|
22
|
+
MODEL
|
|
23
|
+
|
|
24
|
+
inject_into_class 'app/models/entity.rb', 'Entity', model_body
|
|
25
|
+
inject_into_class 'app/models/platform.rb', 'Platform', model_body
|
|
26
|
+
|
|
27
|
+
route "resources :changes, controller: 'paper_trail_manager/changes'"
|
|
28
|
+
route "root to: 'paper_trail_manager/changes#index'"
|
|
29
|
+
|
|
30
|
+
# Allow YAML deserialization of ActiveSupport::TimeWithZone (Ruby 3.1+ Psych 4)
|
|
31
|
+
initializer 'permitted_classes.rb', <<~RUBY
|
|
32
|
+
Rails.application.config.active_record.yaml_column_permitted_classes = [
|
|
33
|
+
ActiveSupport::TimeWithZone,
|
|
34
|
+
ActiveSupport::TimeZone,
|
|
35
|
+
Time
|
|
36
|
+
]
|
|
37
|
+
RUBY
|
|
38
|
+
|
|
39
|
+
rake 'db:migrate db:test:prepare'
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PaperTrailManager, 'authorization integration', versioning: true do
|
|
6
|
+
let(:entity) { FactoryBot.create(:entity, name: 'Test Entity', status: 'Active') }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
entity
|
|
10
|
+
entity.update(status: 'Updated')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after do
|
|
14
|
+
default = proc { true }
|
|
15
|
+
PaperTrailManager.allow_index_block = default
|
|
16
|
+
PaperTrailManager.allow_show_block = default
|
|
17
|
+
PaperTrailManager.allow_revert_block = default
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe 'index' do
|
|
21
|
+
context 'when not authorized' do
|
|
22
|
+
before do
|
|
23
|
+
PaperTrailManager.allow_index_when { |_controller| false }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'redirects with an error flash' do
|
|
27
|
+
get changes_path
|
|
28
|
+
expect(response).to have_http_status(:redirect)
|
|
29
|
+
expect(flash[:error]).to eq('You do not have permission to list changes.')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'show' do
|
|
35
|
+
context 'when not authorized' do
|
|
36
|
+
before do
|
|
37
|
+
PaperTrailManager.allow_show_when { |_controller, _version| false }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'redirects with an error flash' do
|
|
41
|
+
get change_path(entity.versions.last)
|
|
42
|
+
expect(response).to have_http_status(:redirect)
|
|
43
|
+
expect(flash[:error]).to eq('You do not have permission to show that change.')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'when change does not exist' do
|
|
48
|
+
it 'redirects with an error flash' do
|
|
49
|
+
get change_path(id: 999999)
|
|
50
|
+
expect(response).to have_http_status(:redirect)
|
|
51
|
+
expect(flash[:error]).to eq('No such version.')
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe 'revert' do
|
|
57
|
+
context 'when not authorized' do
|
|
58
|
+
before do
|
|
59
|
+
PaperTrailManager.allow_revert_when { |_controller, _version| false }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'redirects with an error flash' do
|
|
63
|
+
put change_path(entity.versions.last)
|
|
64
|
+
expect(response).to have_http_status(:redirect)
|
|
65
|
+
expect(flash[:error]).to eq('You do not have permission to revert this change.')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'when change does not exist' do
|
|
70
|
+
it 'redirects with an error flash' do
|
|
71
|
+
put change_path(id: 999999)
|
|
72
|
+
expect(response).to have_http_status(:redirect)
|
|
73
|
+
expect(flash[:error]).to eq('No such version.')
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe 'filtering by non-existent type' do
|
|
79
|
+
it 'returns an empty changes list' do
|
|
80
|
+
get changes_path(type: 'NonExistentModel')
|
|
81
|
+
expect(response.body).to include('No changes found')
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PaperTrailManager, 'date range filtering', versioning: true do
|
|
6
|
+
let(:entity) { FactoryBot.create(:entity, name: 'Test Entity', status: 'Active') }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
entity
|
|
10
|
+
entity.update(status: 'Updated')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe 'index with date filters' do
|
|
14
|
+
context 'with from parameter' do
|
|
15
|
+
it 'shows changes on or after the date' do
|
|
16
|
+
get changes_path(from: Date.today.to_s)
|
|
17
|
+
expect(response).to have_http_status(:ok)
|
|
18
|
+
expect(response.body).to have_tag('.change_row')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'returns no changes for future date' do
|
|
22
|
+
get changes_path(from: (Date.today + 1).to_s)
|
|
23
|
+
expect(response.body).to include('No changes found')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'with to parameter' do
|
|
28
|
+
it 'shows changes on or before the date' do
|
|
29
|
+
get changes_path(to: Date.today.to_s)
|
|
30
|
+
expect(response).to have_http_status(:ok)
|
|
31
|
+
expect(response.body).to have_tag('.change_row')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'returns no changes for past date' do
|
|
35
|
+
get changes_path(to: (Date.today - 1).to_s)
|
|
36
|
+
expect(response.body).to include('No changes found')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'with from and to combined' do
|
|
41
|
+
it 'shows changes within the range' do
|
|
42
|
+
get changes_path(from: Date.today.to_s, to: Date.today.to_s)
|
|
43
|
+
expect(response).to have_http_status(:ok)
|
|
44
|
+
expect(response.body).to have_tag('.change_row')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'combined with type filter' do
|
|
49
|
+
it 'respects both date and type filters' do
|
|
50
|
+
get changes_path(from: Date.today.to_s, type: 'Entity')
|
|
51
|
+
expect(response).to have_http_status(:ok)
|
|
52
|
+
expect(response.body).to have_tag('.change_item', text: /Entity/)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context 'with invalid date' do
|
|
57
|
+
it 'ignores invalid from date gracefully' do
|
|
58
|
+
get changes_path(from: 'not-a-date')
|
|
59
|
+
expect(response).to have_http_status(:ok)
|
|
60
|
+
expect(response.body).to have_tag('.change_row')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'ignores invalid to date gracefully' do
|
|
64
|
+
get changes_path(to: 'garbage')
|
|
65
|
+
expect(response).to have_http_status(:ok)
|
|
66
|
+
expect(response.body).to have_tag('.change_row')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'date filter form' do
|
|
71
|
+
it 'renders date inputs' do
|
|
72
|
+
get changes_path
|
|
73
|
+
expect(response.body).to have_tag('input[type="date"][name="from"]')
|
|
74
|
+
expect(response.body).to have_tag('input[type="date"][name="to"]')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'preserves date values in form' do
|
|
78
|
+
get changes_path(from: '2026-01-01', to: '2026-12-31')
|
|
79
|
+
expect(response.body).to have_tag('input[name="from"][value="2026-01-01"]')
|
|
80
|
+
expect(response.body).to have_tag('input[name="to"][value="2026-12-31"]')
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
|
-
describe
|
|
4
|
-
it
|
|
5
|
-
::Rails.application.
|
|
5
|
+
describe 'Navigation' do
|
|
6
|
+
it 'is a valid app' do
|
|
7
|
+
expect(::Rails.application).to be_a_kind_of(Rails::Application)
|
|
6
8
|
end
|
|
7
9
|
end
|
|
@@ -1,72 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
describe PaperTrailManager, :versioning => true do
|
|
4
|
-
def version
|
|
5
|
-
return assigns[:version]
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def versions
|
|
9
|
-
return assigns[:versions]
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def item_types
|
|
13
|
-
return versions.map(&:item_type).uniq.sort
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def populate
|
|
17
|
-
@reimu = FactoryGirl.create(:entity, :name => "Miko Hakurei Reimu", :status => "Highly Responsive to Prayers")
|
|
18
|
-
@reimu.update_attributes(:name => "Hakurei Reimu", :status => "Phantasmagoria of Dimensional Dream")
|
|
19
|
-
@reimu.update_attributes(:status => "Perfect Cherry Blossom")
|
|
1
|
+
# frozen_string_literal: true
|
|
20
2
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@flanchan = FactoryGirl.create(:entity, :name => "Flandre Scarlet", :status => "The Embodiment of Scarlet Devil")
|
|
24
|
-
@flanchan.destroy
|
|
25
|
-
|
|
26
|
-
@kyuu_hachi = FactoryGirl.create(:platform, :name => "PC-9801", :status => "SUGOI!!1!")
|
|
27
|
-
@kyuu_hachi.update_attributes(:status => "Kimochi warui.")
|
|
28
|
-
@kyuu_hachi.destroy
|
|
29
|
-
|
|
30
|
-
@uinodouzu = FactoryGirl.create(:platform, :name => "Mikorusofto Uinodouzu", :status => 'o-O')
|
|
31
|
-
end
|
|
3
|
+
require 'spec_helper'
|
|
32
4
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
5
|
+
describe PaperTrailManager, versioning: true do
|
|
6
|
+
context 'without changes' do
|
|
7
|
+
context 'when fetching the index' do
|
|
8
|
+
it 'has no changes by default' do
|
|
9
|
+
get '/changes'
|
|
37
10
|
|
|
38
|
-
|
|
11
|
+
expect(response.body).to include('No changes found')
|
|
39
12
|
end
|
|
40
13
|
end
|
|
41
14
|
end
|
|
42
15
|
|
|
43
|
-
context
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
16
|
+
context 'with changes' do
|
|
17
|
+
let(:reimu) { FactoryBot.create(:entity, name: 'Miko Hakurei Reimu', status: 'Highly Responsive to Prayers') }
|
|
18
|
+
let(:flanchan) { FactoryBot.create(:entity, name: 'Flandre Scarlet', status: 'The Embodiment of Scarlet Devil') }
|
|
19
|
+
let(:sakuya) { FactoryBot.create(:entity, name: 'Sakuya Izayoi', status: 'Flowering Night') }
|
|
20
|
+
let(:kyuu_hachi) { FactoryBot.create(:platform, name: 'PC-9801', status: 'SUGOI!!1!') }
|
|
21
|
+
let!(:uinodouzu) { FactoryBot.create(:platform, name: 'Mikorusofto Uinodouzu', status: 'o-O') }
|
|
22
|
+
|
|
23
|
+
let!(:flanchan_id) { flanchan.id }
|
|
47
24
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
25
|
+
before do
|
|
26
|
+
sakuya
|
|
27
|
+
reimu.update(name: 'Hakurei Reimu', status: 'Phantasmagoria of Dimensional Dream')
|
|
28
|
+
reimu.update(status: 'Perfect Cherry Blossom')
|
|
29
|
+
flanchan.destroy
|
|
30
|
+
kyuu_hachi.update(status: 'Kimochi warui.')
|
|
31
|
+
kyuu_hachi.destroy
|
|
32
|
+
uinodouzu
|
|
52
33
|
end
|
|
53
34
|
|
|
54
|
-
|
|
55
|
-
context
|
|
56
|
-
context
|
|
57
|
-
context
|
|
35
|
+
describe 'index' do
|
|
36
|
+
context 'when getting all changes' do
|
|
37
|
+
context 'with authorization' do
|
|
38
|
+
context 'when getting default index' do
|
|
58
39
|
before { get changes_path }
|
|
59
40
|
|
|
60
|
-
it
|
|
61
|
-
|
|
41
|
+
it 'has all changes' do
|
|
42
|
+
expect(response.body).to have_tag('.change_row', count: 10)
|
|
62
43
|
end
|
|
63
44
|
|
|
64
|
-
it
|
|
65
|
-
|
|
45
|
+
it 'has changes for all changed item types' do
|
|
46
|
+
expect(response.body).to have_tag('.change_item', text: /Entity/)
|
|
47
|
+
expect(response.body).to have_tag('.change_item', text: /Platform/)
|
|
66
48
|
end
|
|
67
49
|
|
|
68
|
-
it
|
|
69
|
-
|
|
50
|
+
it 'orders changes with newest and highest id at the top' do
|
|
51
|
+
ids = response.body.scan(/Change #(\d+)/).flatten.map(&:to_i)
|
|
52
|
+
expect(ids).to eq ids.sort.reverse
|
|
70
53
|
end
|
|
71
54
|
end
|
|
72
55
|
end
|
|
@@ -77,16 +60,17 @@ describe PaperTrailManager, :versioning => true do
|
|
|
77
60
|
# end
|
|
78
61
|
end
|
|
79
62
|
|
|
80
|
-
context
|
|
81
|
-
context
|
|
82
|
-
before { get changes_path(:
|
|
63
|
+
context 'when getting changes for a specific type' do
|
|
64
|
+
context 'when changes exist' do
|
|
65
|
+
before { get changes_path(type: 'Entity') }
|
|
83
66
|
|
|
84
|
-
it
|
|
85
|
-
|
|
67
|
+
it 'shows a subset of the changes' do
|
|
68
|
+
expect(response.body).to have_tag('.change_row', count: 6)
|
|
86
69
|
end
|
|
87
70
|
|
|
88
|
-
it
|
|
89
|
-
|
|
71
|
+
it 'has changes only for that type' do
|
|
72
|
+
expect(response.body).to have_tag('.change_item', text: /Entity/)
|
|
73
|
+
expect(response.body).not_to have_tag('.change_item', text: /Platform/)
|
|
90
74
|
end
|
|
91
75
|
end
|
|
92
76
|
|
|
@@ -96,20 +80,21 @@ describe PaperTrailManager, :versioning => true do
|
|
|
96
80
|
# end
|
|
97
81
|
end
|
|
98
82
|
|
|
99
|
-
context
|
|
100
|
-
context
|
|
101
|
-
before { get changes_path(:
|
|
83
|
+
context 'when getting changes for a specific record' do
|
|
84
|
+
context 'when changes exist' do
|
|
85
|
+
before { get changes_path(type: 'Entity', id: reimu.id) }
|
|
102
86
|
|
|
103
|
-
it
|
|
104
|
-
|
|
87
|
+
it 'shows a subset of the changes' do
|
|
88
|
+
expect(response.body).to have_tag('.change_row', count: 3)
|
|
105
89
|
end
|
|
106
90
|
|
|
107
|
-
it
|
|
108
|
-
|
|
91
|
+
it 'has changes only for that type' do
|
|
92
|
+
expect(response.body).to have_tag('.change_item', text: /Entity/)
|
|
93
|
+
expect(response.body).not_to have_tag('.change_item', text: /Platform/)
|
|
109
94
|
end
|
|
110
95
|
|
|
111
|
-
it
|
|
112
|
-
|
|
96
|
+
it 'has changes only for that record' do
|
|
97
|
+
expect(response.body.scan(%r{/entities/(#{reimu.id})}).flatten.uniq).to eq [reimu.id.to_s]
|
|
113
98
|
end
|
|
114
99
|
end
|
|
115
100
|
|
|
@@ -120,24 +105,25 @@ describe PaperTrailManager, :versioning => true do
|
|
|
120
105
|
end
|
|
121
106
|
end
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
context
|
|
125
|
-
context
|
|
108
|
+
describe 'showing a change' do
|
|
109
|
+
context 'when the change exists' do
|
|
110
|
+
context 'when authorized' do
|
|
111
|
+
let(:version) { reimu.versions.last }
|
|
112
|
+
|
|
126
113
|
before do
|
|
127
|
-
|
|
128
|
-
get change_path(@version)
|
|
114
|
+
get change_path(version)
|
|
129
115
|
end
|
|
130
116
|
|
|
131
|
-
it
|
|
132
|
-
|
|
117
|
+
it 'shows the requested change' do
|
|
118
|
+
expect(response.body).to have_tag('.change_id', text: "Change ##{version.id}")
|
|
133
119
|
end
|
|
134
120
|
|
|
135
|
-
it
|
|
136
|
-
|
|
121
|
+
it 'shows a change with the right event' do
|
|
122
|
+
expect(response.body).to have_tag('.change_event_update')
|
|
137
123
|
end
|
|
138
124
|
|
|
139
|
-
it
|
|
140
|
-
|
|
125
|
+
it 'is associated with the expected record' do
|
|
126
|
+
expect(response.body).to have_tag('.change_item', text: "Entity #{reimu.id}")
|
|
141
127
|
end
|
|
142
128
|
end
|
|
143
129
|
|
|
@@ -152,50 +138,48 @@ describe PaperTrailManager, :versioning => true do
|
|
|
152
138
|
# it "should display an error that the change doesn't exist"
|
|
153
139
|
# end
|
|
154
140
|
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
context "when rolling back changes" do
|
|
158
|
-
context "that that exist" do
|
|
159
|
-
before(:each) { populate }
|
|
160
141
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
142
|
+
describe 'rolling back changes' do
|
|
143
|
+
context 'when changes exist' do
|
|
144
|
+
context 'when authorized' do
|
|
145
|
+
it 'rollbacks a newly-created record by deleting it' do
|
|
146
|
+
expect(Entity).to exist(reimu.id)
|
|
164
147
|
|
|
165
|
-
|
|
148
|
+
put change_path(reimu.versions.first)
|
|
166
149
|
|
|
167
|
-
|
|
168
|
-
|
|
150
|
+
expect(Entity).not_to exist(reimu.id)
|
|
151
|
+
end
|
|
169
152
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
153
|
+
it 'rollbacks an edit by reverting to the previous state' do
|
|
154
|
+
reimu.reload
|
|
155
|
+
expect(reimu.status).to eq 'Perfect Cherry Blossom'
|
|
173
156
|
|
|
174
|
-
|
|
157
|
+
put change_path(reimu.versions.last)
|
|
175
158
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
159
|
+
reimu.reload
|
|
160
|
+
expect(reimu.status).to eq 'Phantasmagoria of Dimensional Dream'
|
|
161
|
+
end
|
|
179
162
|
|
|
180
|
-
|
|
181
|
-
|
|
163
|
+
it 'rollbacks a delete by restoring the record' do
|
|
164
|
+
expect(Entity.exists?(flanchan_id)).to be_falsey
|
|
182
165
|
|
|
183
|
-
|
|
166
|
+
put change_path(PaperTrail::Version.where(item_id: flanchan_id, item_type: 'Entity').last)
|
|
184
167
|
|
|
185
|
-
|
|
186
|
-
|
|
168
|
+
found = Entity.find(flanchan_id)
|
|
169
|
+
expect(found.status).to eq 'The Embodiment of Scarlet Devil'
|
|
170
|
+
end
|
|
187
171
|
end
|
|
172
|
+
|
|
173
|
+
# TODO
|
|
174
|
+
# context "when not authorized" do
|
|
175
|
+
# it "should display an error if user is not allowed to revert that change"
|
|
176
|
+
# end
|
|
188
177
|
end
|
|
189
178
|
|
|
190
179
|
# TODO
|
|
191
|
-
# context "
|
|
192
|
-
# it "should display an error
|
|
180
|
+
# context "that don't exist" do
|
|
181
|
+
# it "should display an error that the change doesn't exist"
|
|
193
182
|
# end
|
|
194
183
|
end
|
|
195
|
-
|
|
196
|
-
# TODO
|
|
197
|
-
# context "that don't exist" do
|
|
198
|
-
# it "should display an error that the change doesn't exist"
|
|
199
|
-
# end
|
|
200
184
|
end
|
|
201
185
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PaperTrailManager, 'response formats', versioning: true do
|
|
6
|
+
let(:entity) { FactoryBot.create(:entity, name: 'Format Test', status: 'Active') }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
entity
|
|
10
|
+
entity.update(status: 'Updated')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe 'JSON format' do
|
|
14
|
+
context 'index' do
|
|
15
|
+
it 'returns valid JSON with version data' do
|
|
16
|
+
get changes_path(format: :json)
|
|
17
|
+
expect(response.content_type).to include('application/json')
|
|
18
|
+
json = JSON.parse(response.body)
|
|
19
|
+
expect(json).to be_an(Array)
|
|
20
|
+
expect(json.length).to be >= 2
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'respects type filter' do
|
|
24
|
+
get changes_path(format: :json, type: 'Entity')
|
|
25
|
+
json = JSON.parse(response.body)
|
|
26
|
+
json.each do |version|
|
|
27
|
+
expect(version['item_type']).to eq('Entity')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'respects id filter' do
|
|
32
|
+
get changes_path(format: :json, type: 'Entity', id: entity.id)
|
|
33
|
+
json = JSON.parse(response.body)
|
|
34
|
+
json.each do |version|
|
|
35
|
+
expect(version['item_id']).to eq(entity.id)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'show' do
|
|
41
|
+
it 'returns valid JSON for a single version' do
|
|
42
|
+
version = entity.versions.last
|
|
43
|
+
get change_path(version, format: :json)
|
|
44
|
+
expect(response.content_type).to include('application/json')
|
|
45
|
+
json = JSON.parse(response.body)
|
|
46
|
+
expect(json['id']).to eq(version.id)
|
|
47
|
+
expect(json['item_type']).to eq('Entity')
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe 'Atom format' do
|
|
53
|
+
context 'index' do
|
|
54
|
+
it 'returns valid XML' do
|
|
55
|
+
get changes_path(format: :atom)
|
|
56
|
+
expect(response.content_type).to include('application/atom+xml')
|
|
57
|
+
expect(response.body).to include('<feed')
|
|
58
|
+
expect(response.body).to include('<entry>')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'includes version entries' do
|
|
62
|
+
get changes_path(format: :atom)
|
|
63
|
+
expect(response.body).to include('Entity')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'respects type filter' do
|
|
67
|
+
get changes_path(format: :atom, type: 'Entity')
|
|
68
|
+
expect(response.body).to include('Entity')
|
|
69
|
+
expect(response.body).not_to include('Platform')
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/spec/rails_helper.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
|
2
|
-
ENV[
|
|
3
|
-
require File.expand_path(
|
|
4
|
-
require
|
|
5
|
-
require
|
|
4
|
+
ENV['RAILS_ENV'] ||= 'test'
|
|
5
|
+
require File.expand_path('dummy/config/environment', __dir__)
|
|
6
|
+
require 'rspec/rails'
|
|
7
|
+
require 'rspec/active_model/mocks'
|
|
6
8
|
|
|
7
9
|
RSpec.configure do |config|
|
|
8
10
|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|