split my_page module off of grids

pull/7185/head
Jens Ulferts 6 years ago
parent 02e26879ac
commit aaa7216d80
No known key found for this signature in database
GPG Key ID: 3CAA4B1182CF5308
  1. 7
      Gemfile.lock
  2. 1
      Gemfile.modules
  3. 3
      modules/grids/app/controllers/api/v3/grids/schemas/grid_schema_representer.rb
  4. 14
      modules/grids/bin/rails
  5. 4
      modules/grids/lib/grids/engine.rb
  6. 26
      modules/grids/spec/contracts/grids/create_contract_spec.rb
  7. 256
      modules/grids/spec/contracts/grids/shared_examples.rb
  8. 2
      modules/grids/spec/contracts/grids/update_contract_spec.rb
  9. 24
      modules/grids/spec/factories/grid_factory.rb
  10. 4
      modules/grids/spec/lib/api/v3/grids/grid_payload_representer_parsing_spec.rb
  11. 25
      modules/grids/spec/lib/api/v3/grids/grid_representer_rendering_spec.rb
  12. 140
      modules/grids/spec/requests/api/v3/grids/grids_create_form_resource_spec.rb
  13. 353
      modules/grids/spec/requests/api/v3/grids/grids_resource_spec.rb
  14. 124
      modules/grids/spec/requests/api/v3/grids/grids_update_form_resource_spec.rb
  15. 4
      modules/grids/spec/services/grids/create_service_spec.rb
  16. 2
      modules/grids/spec/services/grids/set_attributes_service_spec.rb
  17. 2
      modules/grids/spec/services/grids/update_service_spec.rb
  18. 7
      modules/my_page/.gitignore
  19. 3
      modules/my_page/Gemfile
  20. 0
      modules/my_page/app/models/grids/my_page.rb
  21. 4
      modules/my_page/lib/my_page.rb
  22. 11
      modules/my_page/lib/my_page/engine.rb
  23. 4
      modules/my_page/lib/my_page/grid_registration.rb
  24. 12
      modules/my_page/my_page.gemspec
  25. 61
      modules/my_page/spec/contracts/grids/create_contract_spec.rb
  26. 320
      modules/my_page/spec/contracts/grids/shared_examples.rb
  27. 39
      modules/my_page/spec/contracts/grids/update_contract_spec.rb
  28. 25
      modules/my_page/spec/factories/grid_factory.rb
  29. 0
      modules/my_page/spec/features/my/accountable_spec.rb
  30. 0
      modules/my_page/spec/features/my/assigned_to_me_spec.rb
  31. 0
      modules/my_page/spec/features/my/documents_spec.rb
  32. 0
      modules/my_page/spec/features/my/my_page_spec.rb
  33. 0
      modules/my_page/spec/features/my/news_spec.rb
  34. 0
      modules/my_page/spec/features/my/time_entries_current_user_spec.rb
  35. 0
      modules/my_page/spec/features/my/work_package_table_spec.rb
  36. 0
      modules/my_page/spec/models/grids/my_page_spec.rb
  37. 77
      modules/my_page/spec/models/grids/shared_model.rb
  38. 0
      modules/my_page/spec/queries/grids/filters/scope_filter_spec.rb
  39. 0
      modules/my_page/spec/queries/grids/query_integration_spec.rb
  40. 193
      modules/my_page/spec/requests/api/v3/grids/grids_create_form_resource_spec.rb
  41. 414
      modules/my_page/spec/requests/api/v3/grids/grids_resource_spec.rb
  42. 174
      modules/my_page/spec/requests/api/v3/grids/grids_update_form_resource_spec.rb

@ -180,6 +180,12 @@ PATH
openproject-meeting (1.0.0)
icalendar (~> 2.5.0)
PATH
remote: modules/my_page
specs:
my_page (1.0.0)
grids
PATH
remote: modules/my_project_page
specs:
@ -970,6 +976,7 @@ DEPENDENCIES
lograge (~> 0.10.0)
meta-tags (~> 2.11.0)
multi_json (~> 1.13.1)
my_page!
mysql2 (~> 0.5.0)
net-ldap (~> 0.16.0)
newrelic_rpm

@ -40,6 +40,7 @@ group :opf_plugins do
gem 'openproject-ldap_groups', path: 'modules/ldap_groups'
gem 'grids', path: 'modules/grids'
gem 'my_page', path: 'modules/my_page'
gem 'openproject-boards', path: 'modules/boards'
gem 'openproject-bim_seeder', path: 'modules/bim_seeder', require: !!(ENV['OPENPROJECT_EDITION'] == 'bim')

@ -76,8 +76,7 @@ module API
value_representer: false,
link_factory: ->(path) {
{
href: path,
title: I18n.t(:label_my_page)
href: path
}
}

@ -1,14 +0,0 @@
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails gems
# installed from the root of your application.
ENGINE_ROOT = File.expand_path('../..', __FILE__)
ENGINE_PATH = File.expand_path('../../lib/grids/engine', __FILE__)
APP_PATH = File.expand_path('../../test/dummy/config/application', __FILE__)
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
require 'rails/all'
require 'rails/engine/commands'

@ -9,9 +9,5 @@ module Grids
Queries::Register.filter query, Grids::Filters::ScopeFilter
end
config.to_prepare do
Grids::MyPageGridRegistration.register!
end
end
end

@ -38,9 +38,11 @@ describe Grids::CreateContract do
it_behaves_like 'shared grid contract attributes'
describe 'type' do
let(:grid) { FactoryBot.build_stubbed(:grid, default_values) }
it_behaves_like 'is writable' do
let(:attribute) { :type }
let(:value) { 'Grids::MyPage' }
let(:value) { 'Grids::Grid' }
end
end
@ -51,15 +53,6 @@ describe Grids::CreateContract do
let(:attribute) { :user_id }
let(:value) { 5 }
end
context 'for a Grids::MyPage' do
let(:grid) { FactoryBot.build_stubbed(:my_page, default_values) }
it_behaves_like 'is writable' do
let(:attribute) { :user_id }
let(:value) { 5 }
end
end
end
describe 'project_id' do
@ -69,15 +62,6 @@ describe Grids::CreateContract do
let(:attribute) { :project_id }
let(:value) { 5 }
end
context 'for a Grids::MyPage' do
let(:grid) { FactoryBot.build_stubbed(:my_page, default_values) }
it_behaves_like 'is not writable' do
let(:attribute) { :project_id }
let(:value) { 5 }
end
end
end
describe '#assignable_values' do
@ -104,12 +88,12 @@ describe Grids::CreateContract do
allow(Grids::Configuration)
.to receive(:allowed_widget?)
.with(Grids::MyPage, :widget1, user)
.with(Grids::Grid, :widget1, user)
.and_return(true)
allow(Grids::Configuration)
.to receive(:allowed_widget?)
.with(Grids::MyPage, :widget2, user)
.with(Grids::Grid, :widget2, user)
.and_return(false)
expect(instance.assignable_values(:widgets, user))

@ -39,7 +39,7 @@ shared_context 'grid contract' do
}
end
let(:grid) do
FactoryBot.build_stubbed(:my_page, default_values)
FactoryBot.build_stubbed(:grid, default_values)
end
shared_examples_for 'validates positive integer' do
@ -113,261 +113,7 @@ shared_examples_for 'shared grid contract attributes' do
end
end
describe 'widgets' do
it_behaves_like 'is writable' do
let(:attribute) { :widgets }
let(:value) do
[
Grids::Widget.new(start_row: 1,
end_row: 4,
start_column: 2,
end_column: 5,
identifier: 'work_packages_assigned')
]
end
end
context 'invalid identifier' do
before do
grid.widgets.build(start_row: 1,
end_row: 4,
start_column: 2,
end_column: 5,
identifier: 'bogus_identifier')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :inclusion }]
end
end
context 'collisions between widgets' do
before do
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
grid.widgets.build(start_row: 2,
end_row: 4,
start_column: 2,
end_column: 4,
identifier: 'work_packages_created')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :overlaps }, { error: :overlaps }]
end
end
context 'widgets having the same start column as another\'s end column' do
before do
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 3,
end_column: 4,
identifier: 'work_packages_created')
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'widgets having the same start row as another\'s end row' do
before do
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
grid.widgets.build(start_row: 3,
end_row: 4,
start_column: 1,
end_column: 3,
identifier: 'work_packages_created')
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'widgets being outside (max) of the grid' do
before do
grid.widgets.build(start_row: 1,
end_row: grid.row_count + 2,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :outside }]
end
end
context 'widgets being outside (min) of the grid' do
before do
grid.widgets.build(start_row: 1,
end_row: 2,
start_column: -1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :outside }]
end
end
context 'widgets spanning the whole grid' do
before do
grid.widgets.build(start_row: 1,
end_row: grid.row_count + 1,
start_column: 1,
end_column: grid.column_count + 1,
identifier: 'work_packages_assigned')
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'widgets having start after end column' do
before do
grid.widgets.build(start_row: 1,
end_row: 2,
start_column: 4,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
context 'widgets having start after end row' do
before do
grid.widgets.build(start_row: 4,
end_row: 2,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
context 'widgets having start equals end column' do
before do
grid.widgets.build(start_row: 1,
end_row: 2,
start_column: 4,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
context 'widgets having start equals end row' do
before do
grid.widgets.build(start_row: 2,
end_row: 2,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
end
describe 'valid grid subclasses' do
context 'for a registered subclass' do
let(:grid) do
FactoryBot.build_stubbed(:my_page, default_values)
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'for the Grid superclass itself' do
let(:grid) do
FactoryBot.build_stubbed(:grid, default_values)

@ -51,7 +51,7 @@ describe Grids::UpdateContract do
instance.validate
# scope because that is what type is called on the outside for grids
expect(instance.errors.details[:scope])
.to match_array [{ error: :error_readonly }]
.to match_array [{ error: :error_readonly }, { error: :inclusion }]
end
end

@ -1,28 +1,4 @@
FactoryBot.define do
factory :grid, class: Grids::Grid do
end
factory :my_page, class: Grids::MyPage do
user
row_count { 7 }
column_count { 4 }
widgets do
[
Grids::Widget.new(
identifier: 'work_packages_assigned',
start_row: 1,
end_row: 7,
start_column: 1,
end_column: 3
),
Grids::Widget.new(
identifier: 'work_packages_created',
start_row: 1,
end_row: 7,
start_column: 3,
end_column: 5
)
]
end
end
end

@ -71,7 +71,7 @@ describe ::API::V3::Grids::GridPayloadRepresenter, 'parsing' do
],
"_links" => {
"scope" => {
"href" => my_page_path
"href" => 'some_path'
}
}
}
@ -82,7 +82,7 @@ describe ::API::V3::Grids::GridPayloadRepresenter, 'parsing' do
it 'updates page' do
grid = representer.from_hash(hash)
expect(grid.scope)
.to eql(my_page_path)
.to eql('some_path')
end
end
end

@ -33,7 +33,7 @@ describe ::API::V3::Grids::GridRepresenter, 'rendering' do
let(:grid) do
FactoryBot.build_stubbed(
:my_page,
:grid,
row_count: 4,
column_count: 5,
widgets: [
@ -68,6 +68,21 @@ describe ::API::V3::Grids::GridRepresenter, 'rendering' do
let(:current_user) { FactoryBot.build_stubbed(:user) }
let(:representer) { described_class.new(grid, current_user: current_user) }
let(:writable) { true }
let(:scope_path) { 'bogus_scope' }
before do
allow(::Grids::Configuration)
.to receive(:writable?)
.with(grid, current_user)
.and_return(writable)
allow(::Grids::Configuration)
.to receive(:to_scope)
.with(Grids::Grid, [])
.and_return(scope_path)
end
context 'generation' do
subject(:generated) { representer.to_json }
@ -78,12 +93,6 @@ describe ::API::V3::Grids::GridRepresenter, 'rendering' do
.at_path('_type')
end
it 'identifies the url the grid is stored for' do
is_expected
.to be_json_eql(my_page_path.to_json)
.at_path('_links/scope/href')
end
it 'has an id' do
is_expected
.to be_json_eql(grid.id)
@ -180,7 +189,7 @@ describe ::API::V3::Grids::GridRepresenter, 'rendering' do
context 'scope link' do
it_behaves_like 'has an untitled link' do
let(:link) { 'scope' }
let(:href) { my_page_path }
let(:href) { scope_path }
let(:type) { "text/html" }
it 'has a content type of html' do

@ -61,16 +61,6 @@ describe "POST /api/v3/grids/form", type: :request, content_type: :json do
.at_path('_type')
end
it 'contains a Schema embedding the available values' do
expect(subject.body)
.to be_json_eql("Schema".to_json)
.at_path('_embedded/schema/_type')
expect(subject.body)
.to be_json_eql(my_page_path.to_json)
.at_path('_embedded/schema/scope/_links/allowedValues/0/href')
end
it 'contains default data in the payload' do
expected = {
"rowCount": 4,
@ -95,135 +85,5 @@ describe "POST /api/v3/grids/form", type: :request, content_type: :json do
expect(subject.body)
.not_to have_json_path('_links/commit')
end
context 'with /my/page for the scope value' do
let(:params) do
{
'_links': {
'scope': {
'href': my_page_path
}
}
}
end
it 'contains default data in the payload' do
expected = {
"rowCount": 7,
"columnCount": 4,
"options": {},
"widgets": [
{
"_type": "GridWidget",
identifier: 'work_packages_assigned',
"options": {},
startRow: 1,
endRow: 7,
startColumn: 1,
endColumn: 3
},
{
"_type": "GridWidget",
identifier: 'work_packages_created',
"options": {},
startRow: 1,
endRow: 7,
startColumn: 3,
endColumn: 5
}
],
"_links": {
"scope": {
"href": "/my/page",
"type": "text/html"
}
}
}
expect(subject.body)
.to be_json_eql(expected.to_json)
.at_path('_embedded/payload')
end
it 'has no validationErrors' do
expect(subject.body)
.to be_json_eql({}.to_json)
.at_path('_embedded/validationErrors')
end
it 'has a commit link' do
expect(subject.body)
.to be_json_eql(api_v3_paths.grids.to_json)
.at_path('_links/commit/href')
end
end
context 'with an unsupported widget identifier' do
let(:params) do
{
'_links': {
'scope': {
'href': my_page_path
}
},
"widgets": [
{
"_type": "GridWidget",
"identifier": "bogus_identifier",
"startRow": 4,
"endRow": 5,
"startColumn": 1,
"endColumn": 2
}
]
}
end
it 'has a validationError on widget' do
expect(subject.body)
.to be_json_eql("Widgets is not set to one of the allowed values.".to_json)
.at_path('_embedded/validationErrors/widgets/message')
end
end
context 'with name set' do
let(:params) do
{
name: 'My custom grid 1',
'_links': {
'scope': {
'href': my_page_path
}
}
}
end
it 'feeds it back' do
expect(subject.body)
.to be_json_eql("My custom grid 1".to_json)
.at_path('_embedded/payload/name')
end
end
context 'with options set' do
let(:params) do
{
options: {
foo: 'bar'
},
'_links': {
'scope': {
'href': my_page_path
}
}
}
end
it 'feeds them back' do
expect(subject.body)
.to be_json_eql("bar".to_json)
.at_path('_embedded/payload/options/foo')
end
end
end
end

@ -37,12 +37,6 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
FactoryBot.create(:user)
end
let(:my_page_grid) { FactoryBot.create(:my_page, user: current_user) }
let(:other_user) do
FactoryBot.create(:user)
end
let(:other_my_page_grid) { FactoryBot.create(:my_page, user: other_user) }
before do
login_as(current_user)
end
@ -52,369 +46,22 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
describe '#get INDEX' do
let(:path) { api_v3_paths.grids }
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
before do
stored_grids
get path
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends a collection of grids but only those visible to the current user' do
expect(subject.body)
.to be_json_eql('Collection'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_embedded/elements/0/_type')
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('total')
end
context 'with a filter on the scope attribute' do
shared_let(:other_grid) do
grid = Grids::Grid.new(row_count: 20,
column_count: 20)
grid.save
Grids::Grid
.where(id: grid.id)
.update_all(user_id: current_user.id)
grid
end
let(:stored_grids) do
my_page_grid
other_my_page_grid
other_grid
end
let(:path) do
filter = [{ 'scope' =>
{
'operator' => '=',
'values' => [my_page_path]
} }]
"#{api_v3_paths.grids}?#{{ filters: filter.to_json }.to_query}"
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends only the my page of the current user' do
expect(subject.body)
.to be_json_eql('Collection'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_embedded/elements/0/_type')
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('total')
end
end
end
describe '#get' do
let(:path) { api_v3_paths.grid(my_page_grid.id) }
let(:stored_grids) do
my_page_grid
end
before do
stored_grids
get path
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends a grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
end
it 'identifies the url the grid is stored for' do
expect(subject.body)
.to be_json_eql(my_page_path.to_json)
.at_path('_links/scope/href')
end
context 'with the page not existing' do
let(:path) { api_v3_paths.grid(5) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
context 'with the grid belonging to someone else' do
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
let(:path) { api_v3_paths.grid(other_my_page_grid.id) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
end
describe '#patch' do
let(:path) { api_v3_paths.grid(my_page_grid.id) }
let(:params) do
{
"rowCount": 10,
"name": 'foo',
"columnCount": 15,
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}]
}.with_indifferent_access
end
let(:stored_grids) do
my_page_grid
end
before do
stored_grids
patch path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'returns the altered grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('foo'.to_json)
.at_path('name')
expect(subject.body)
.to be_json_eql(params['rowCount'].to_json)
.at_path('rowCount')
expect(subject.body)
.to be_json_eql(params['widgets'][0]['identifier'].to_json)
.at_path('widgets/0/identifier')
end
it 'perists the changes' do
expect(my_page_grid.reload.row_count)
.to eql params['rowCount']
end
context 'with invalid params' do
let(:params) do
{
"rowCount": -5,
"columnCount": 15,
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}]
}.with_indifferent_access
end
it 'responds with 422 and mentions the error' do
expect(subject.status).to eq 422
expect(subject.body)
.to be_json_eql('Error'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql("Widgets is outside of the grid.".to_json)
.at_path('_embedded/errors/0/message')
expect(subject.body)
.to be_json_eql("Number of rows must be greater than 0.".to_json)
.at_path('_embedded/errors/1/message')
end
it 'does not persist the changes to widgets' do
expect(my_page_grid.reload.widgets.count)
.to eql Grids::MyPageGridRegistration.defaults[:widgets].size
end
end
context 'with a scope param' do
let(:params) do
{
"_links": {
"scope": {
"href": ''
}
}
}.with_indifferent_access
end
it 'responds with 422 and mentions the error' do
expect(subject.status).to eq 422
expect(subject.body)
.to be_json_eql('Error'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql("You must not write a read-only attribute.".to_json)
.at_path('message')
expect(subject.body)
.to be_json_eql("scope".to_json)
.at_path('_embedded/details/attribute')
end
end
context 'with the page not existing' do
let(:path) { api_v3_paths.grid(5) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
context 'with the grid belonging to someone else' do
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
let(:path) { api_v3_paths.grid(other_my_page_grid.id) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
end
describe '#post' do
let(:path) { api_v3_paths.grids }
let(:params) do
{
"rowCount": 10,
"columnCount": 15,
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}],
"_links": {
"scope": {
"href": my_page_path
}
}
}.with_indifferent_access
end
before do
post path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'responds with 201 CREATED' do
expect(subject.status).to eq(201)
end
it 'returns the created grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql(params['rowCount'].to_json)
.at_path('rowCount')
expect(subject.body)
.to be_json_eql(params['widgets'][0]['identifier'].to_json)
.at_path('widgets/0/identifier')
end
it 'persists the grid' do
expect(Grids::Grid.count)
.to eql(1)
end
context 'with invalid params' do
let(:params) do
{
"rowCount": -5,
"columnCount": "sdjfksdfsdfdsf",
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}],
"_links": {
"scope": {
"href": my_page_path
}
}
}.with_indifferent_access
end
it 'responds with 422' do
expect(subject.status).to eq(422)
end
it 'does not create a grid' do
expect(Grids::Grid.count)
.to eql(0)
end
it 'returns the errors' do
expect(subject.body)
.to be_json_eql('Error'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql("Widgets is outside of the grid.".to_json)
.at_path('_embedded/errors/0/message')
expect(subject.body)
.to be_json_eql("Number of rows must be greater than 0.".to_json)
.at_path('_embedded/errors/1/message')
expect(subject.body)
.to be_json_eql("Number of columns must be greater than 0.".to_json)
.at_path('_embedded/errors/2/message')
end
end
context 'without a page link' do
let(:params) do
{

@ -37,10 +37,6 @@ describe "PATCH /api/v3/grids/:id/form", type: :request, content_type: :json do
FactoryBot.create(:user)
end
let(:grid) do
FactoryBot.create(:my_page, user: current_user)
end
let(:path) { api_v3_paths.grid_form(grid.id) }
let(:params) { {} }
subject(:response) { last_response }
@ -53,126 +49,8 @@ describe "PATCH /api/v3/grids/:id/form", type: :request, content_type: :json do
post path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'returns 200 OK' do
expect(subject.status)
.to eql 200
end
it 'is of type form' do
expect(subject.body)
.to be_json_eql("Form".to_json)
.at_path('_type')
end
it 'contains a Schema disallowing setting scope' do
expect(subject.body)
.to be_json_eql("Schema".to_json)
.at_path('_embedded/schema/_type')
expect(subject.body)
.to be_json_eql(false.to_json)
.at_path('_embedded/schema/scope/writable')
end
it 'contains the current data in the payload' do
expected = {
rowCount: 7,
columnCount: 4,
options: {},
widgets: [
{
"_type": "GridWidget",
identifier: 'work_packages_assigned',
options: {},
startRow: 1,
endRow: 7,
startColumn: 1,
endColumn: 3
},
{
"_type": "GridWidget",
identifier: 'work_packages_created',
options: {},
startRow: 1,
endRow: 7,
startColumn: 3,
endColumn: 5
}
],
"_links": {
"scope": {
"href": "/my/page",
"type": "text/html"
}
}
}
expect(subject.body)
.to be_json_eql(expected.to_json)
.at_path('_embedded/payload')
end
it 'has a commit link' do
expect(subject.body)
.to be_json_eql(api_v3_paths.grid(grid.id).to_json)
.at_path('_links/commit/href')
end
context 'with some value for the scope value' do
let(:params) do
{
'_links': {
'scope': {
'href': '/some/path'
}
}
}
end
it 'has a validation error on scope as the value is not writeable' do
expect(subject.body)
.to be_json_eql("You must not write a read-only attribute.".to_json)
.at_path('_embedded/validationErrors/scope/message')
end
end
context 'with an unsupported widget identifier' do
let(:params) do
{
"widgets": [
{
"_type": "GridWidget",
"identifier": "bogus_identifier",
"startRow": 4,
"endRow": 5,
"startColumn": 1,
"endColumn": 2
}
]
}
end
it 'has a validationError on widget' do
expect(subject.body)
.to be_json_eql("Widgets is not set to one of the allowed values.".to_json)
.at_path('_embedded/validationErrors/widgets/message')
end
end
context 'for a non existing grid' do
let(:path) { api_v3_paths.grid_form(grid.id + 5) }
it 'returns 404 NOT FOUND' do
expect(subject.status)
.to eql 404
end
end
context 'for another user\'s grid' do
let(:other_user) { FactoryBot.create(:user) }
let(:other_grid) { FactoryBot.create(:my_page, user: other_user) }
let(:path) { api_v3_paths.grid_form(other_grid.id) }
let(:path) { api_v3_paths.grid_form(5) }
it 'returns 404 NOT FOUND' do
expect(subject.status)

@ -50,7 +50,9 @@ describe Grids::CreateService, type: :model do
end
let(:scope) { "some/scope/url" }
let(:call_attributes) { { scope: scope } }
let(:grid_class) { Grids::MyPage }
let(:grid_class) do
Grids::Grid
end
let(:set_attributes_success) do
true
end

@ -56,7 +56,7 @@ describe Grids::SetAttributesService, type: :model do
contract_class: contract_class)
end
let(:call_attributes) { {} }
let(:grid_class) { Grids::MyPage }
let(:grid_class) { Grids::Grid }
let(:grid) do
FactoryBot.build_stubbed(grid_class.name.demodulize.underscore.to_sym, widgets: [])
end

@ -42,7 +42,7 @@ describe Grids::UpdateService, type: :model do
contract_class: contract_class)
end
let(:call_attributes) { {} }
let(:grid_class) { Grids::MyPage }
let(:grid_class) { Grids::Grid }
let(:set_attributes_success) do
true
end

@ -0,0 +1,7 @@
.bundle/
log/*.log
pkg/
test/dummy/db/*.sqlite3
test/dummy/db/*.sqlite3-journal
test/dummy/log/*.log
test/dummy/tmp/

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

@ -0,0 +1,4 @@
require "my_page/engine"
module MyPage
end

@ -0,0 +1,11 @@
module MyPage
class Engine < ::Rails::Engine
isolate_namespace MyPage
include OpenProject::Plugins::ActsAsOpEngine
config.to_prepare do
MyPage::GridRegistration.register!
end
end
end

@ -1,5 +1,5 @@
module Grids
class MyPageGridRegistration < ::Grids::Configuration::Registration
module MyPage
class GridRegistration < ::Grids::Configuration::Registration
grid_class 'Grids::MyPage'
to_scope :my_page_path

@ -0,0 +1,12 @@
# encoding: UTF-8
Gem::Specification.new do |s|
s.name = "my_page"
s.version = '1.0.0'
s.authors = ["OpenProject"]
s.summary = "OpenProject MyPage."
s.files = Dir["{app,config,db,lib}/**/*"]
s.add_dependency 'grids'
end

@ -0,0 +1,61 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require_relative './shared_examples'
describe Grids::CreateContract do
include_context 'grid contract'
include_context 'model contract'
it_behaves_like 'shared grid contract attributes'
describe 'user_id' do
context 'for a Grids::MyPage' do
let(:grid) { FactoryBot.build_stubbed(:my_page, default_values) }
it_behaves_like 'is writable' do
let(:attribute) { :user_id }
let(:value) { 5 }
end
end
end
describe 'project_id' do
context 'for a Grids::MyPage' do
let(:grid) { FactoryBot.build_stubbed(:my_page, default_values) }
it_behaves_like 'is not writable' do
let(:attribute) { :project_id }
let(:value) { 5 }
end
end
end
end

@ -0,0 +1,320 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
shared_context 'grid contract' do
let(:user) { FactoryBot.build_stubbed(:user) }
let(:instance) { described_class.new(grid, user) }
let(:default_values) do
{
row_count: 6,
column_count: 7,
widgets: []
}
end
let(:grid) do
FactoryBot.build_stubbed(:my_page, default_values)
end
end
shared_examples_for 'shared grid contract attributes' do
include_context 'model contract'
let(:model) { grid }
describe 'widgets' do
it_behaves_like 'is writable' do
let(:attribute) { :widgets }
let(:value) do
[
Grids::Widget.new(start_row: 1,
end_row: 4,
start_column: 2,
end_column: 5,
identifier: 'work_packages_assigned')
]
end
end
context 'invalid identifier' do
before do
grid.widgets.build(start_row: 1,
end_row: 4,
start_column: 2,
end_column: 5,
identifier: 'bogus_identifier')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :inclusion }]
end
end
context 'collisions between widgets' do
before do
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
grid.widgets.build(start_row: 2,
end_row: 4,
start_column: 2,
end_column: 4,
identifier: 'work_packages_created')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :overlaps }, { error: :overlaps }]
end
end
context 'widgets having the same start column as another\'s end column' do
before do
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 3,
end_column: 4,
identifier: 'work_packages_created')
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'widgets having the same start row as another\'s end row' do
before do
grid.widgets.build(start_row: 1,
end_row: 3,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
grid.widgets.build(start_row: 3,
end_row: 4,
start_column: 1,
end_column: 3,
identifier: 'work_packages_created')
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'widgets being outside (max) of the grid' do
before do
grid.widgets.build(start_row: 1,
end_row: grid.row_count + 2,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :outside }]
end
end
context 'widgets being outside (min) of the grid' do
before do
grid.widgets.build(start_row: 1,
end_row: 2,
start_column: -1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :outside }]
end
end
context 'widgets spanning the whole grid' do
before do
grid.widgets.build(start_row: 1,
end_row: grid.row_count + 1,
start_column: 1,
end_column: grid.column_count + 1,
identifier: 'work_packages_assigned')
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'widgets having start after end column' do
before do
grid.widgets.build(start_row: 1,
end_row: 2,
start_column: 4,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
context 'widgets having start after end row' do
before do
grid.widgets.build(start_row: 4,
end_row: 2,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
context 'widgets having start equals end column' do
before do
grid.widgets.build(start_row: 1,
end_row: 2,
start_column: 4,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
context 'widgets having start equals end row' do
before do
grid.widgets.build(start_row: 2,
end_row: 2,
start_column: 1,
end_column: 3,
identifier: 'work_packages_assigned')
end
it 'is invalid' do
expect(instance.validate)
.to be_falsey
end
it 'notes the error' do
instance.validate
expect(instance.errors.details[:widgets])
.to match_array [{ error: :end_before_start }]
end
end
end
describe 'valid grid subclasses' do
context 'for a registered subclass' do
let(:grid) do
FactoryBot.build_stubbed(:my_page, default_values)
end
it 'is valid' do
expect(instance.validate)
.to be_truthy
end
end
context 'for the Grid superclass itself' do
let(:grid) do
FactoryBot.build_stubbed(:grid, default_values)
end
before do
instance.validate
end
it 'is invalid for the grid superclass itself' do
expect(instance.errors.details[:scope])
.to match_array [{ error: :inclusion }]
end
end
end
end

@ -0,0 +1,39 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require_relative './shared_examples'
describe Grids::UpdateContract do
include_context 'model contract'
include_context 'grid contract'
it_behaves_like 'shared grid contract attributes'
end

@ -0,0 +1,25 @@
FactoryBot.define do
factory :my_page, class: Grids::MyPage do
user
row_count { 7 }
column_count { 4 }
widgets do
[
Grids::Widget.new(
identifier: 'work_packages_assigned',
start_row: 1,
end_row: 7,
start_column: 1,
end_column: 3
),
Grids::Widget.new(
identifier: 'work_packages_created',
start_row: 1,
end_row: 7,
start_column: 3,
end_column: 5
)
]
end
end
end

@ -0,0 +1,77 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
shared_examples_for 'grid attributes' do
describe 'attributes' do
it '#row_count' do
instance.row_count = 5
expect(instance.row_count)
.to eql 5
end
it '#column_count' do
instance.column_count = 5
expect(instance.column_count)
.to eql 5
end
it '#name' do
instance.name = 'custom 123'
expect(instance.name)
.to eql 'custom 123'
# can be empty
instance.name = nil
expect(instance).to be_valid
end
it '#options' do
value = {
some: 'value',
and: {
also: 1
}
}
instance.options = value
expect(instance.options)
.to eql value
end
it '#widgets' do
widgets = [
Grids::Widget.new(start_row: 2),
Grids::Widget.new(start_row: 5)
]
instance.widgets = widgets
expect(instance.widgets)
.to match_array widgets
end
end
end

@ -0,0 +1,193 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'rack/test'
describe "POST /api/v3/grids/form", type: :request, content_type: :json do
include Rack::Test::Methods
include API::V3::Utilities::PathHelper
shared_let(:current_user) do
FactoryBot.create(:user)
end
let(:path) { api_v3_paths.create_grid_form }
let(:params) { {} }
subject(:response) { last_response }
before do
login_as(current_user)
end
describe '#post' do
before do
post path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'contains a Schema embedding the available values' do
expect(subject.body)
.to be_json_eql("Schema".to_json)
.at_path('_embedded/schema/_type')
expect(subject.body)
.to be_json_eql(my_page_path.to_json)
.at_path('_embedded/schema/scope/_links/allowedValues/0/href')
end
context 'with /my/page for the scope value' do
let(:params) do
{
'_links': {
'scope': {
'href': my_page_path
}
}
}
end
it 'contains default data in the payload' do
expected = {
"rowCount": 7,
"columnCount": 4,
"options": {},
"widgets": [
{
"_type": "GridWidget",
identifier: 'work_packages_assigned',
"options": {},
startRow: 1,
endRow: 7,
startColumn: 1,
endColumn: 3
},
{
"_type": "GridWidget",
identifier: 'work_packages_created',
"options": {},
startRow: 1,
endRow: 7,
startColumn: 3,
endColumn: 5
}
],
"_links": {
"scope": {
"href": "/my/page",
"type": "text/html"
}
}
}
expect(subject.body)
.to be_json_eql(expected.to_json)
.at_path('_embedded/payload')
end
it 'has no validationErrors' do
expect(subject.body)
.to be_json_eql({}.to_json)
.at_path('_embedded/validationErrors')
end
it 'has a commit link' do
expect(subject.body)
.to be_json_eql(api_v3_paths.grids.to_json)
.at_path('_links/commit/href')
end
end
context 'with an unsupported widget identifier' do
let(:params) do
{
'_links': {
'scope': {
'href': my_page_path
}
},
"widgets": [
{
"_type": "GridWidget",
"identifier": "bogus_identifier",
"startRow": 4,
"endRow": 5,
"startColumn": 1,
"endColumn": 2
}
]
}
end
it 'has a validationError on widget' do
expect(subject.body)
.to be_json_eql("Widgets is not set to one of the allowed values.".to_json)
.at_path('_embedded/validationErrors/widgets/message')
end
end
context 'with name set' do
let(:params) do
{
name: 'My custom grid 1',
'_links': {
'scope': {
'href': my_page_path
}
}
}
end
it 'feeds it back' do
expect(subject.body)
.to be_json_eql("My custom grid 1".to_json)
.at_path('_embedded/payload/name')
end
end
context 'with options set' do
let(:params) do
{
options: {
foo: 'bar'
},
'_links': {
'scope': {
'href': my_page_path
}
}
}
end
it 'feeds them back' do
expect(subject.body)
.to be_json_eql("bar".to_json)
.at_path('_embedded/payload/options/foo')
end
end
end
end

@ -0,0 +1,414 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'rack/test'
describe 'API v3 Grids resource', type: :request, content_type: :json do
include Rack::Test::Methods
include API::V3::Utilities::PathHelper
shared_let(:current_user) do
FactoryBot.create(:user)
end
let(:my_page_grid) { FactoryBot.create(:my_page, user: current_user) }
let(:other_user) do
FactoryBot.create(:user)
end
let(:other_my_page_grid) { FactoryBot.create(:my_page, user: other_user) }
before do
login_as(current_user)
end
subject(:response) { last_response }
describe '#get INDEX' do
let(:path) { api_v3_paths.grids }
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
before do
stored_grids
get path
end
it 'sends a collection of grids but only those visible to the current user' do
expect(subject.body)
.to be_json_eql('Collection'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_embedded/elements/0/_type')
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('total')
end
context 'with a filter on the scope attribute' do
shared_let(:other_grid) do
grid = Grids::Grid.new(row_count: 20,
column_count: 20)
grid.save
Grids::Grid
.where(id: grid.id)
.update_all(user_id: current_user.id)
grid
end
let(:stored_grids) do
my_page_grid
other_my_page_grid
other_grid
end
let(:path) do
filter = [{ 'scope' =>
{
'operator' => '=',
'values' => [my_page_path]
} }]
"#{api_v3_paths.grids}?#{{ filters: filter.to_json }.to_query}"
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends only the my page of the current user' do
expect(subject.body)
.to be_json_eql('Collection'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_embedded/elements/0/_type')
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('total')
end
end
end
describe '#get' do
let(:path) { api_v3_paths.grid(my_page_grid.id) }
let(:stored_grids) do
my_page_grid
end
before do
stored_grids
get path
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends a grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
end
it 'identifies the url the grid is stored for' do
expect(subject.body)
.to be_json_eql(my_page_path.to_json)
.at_path('_links/scope/href')
end
context 'with the page not existing' do
let(:path) { api_v3_paths.grid(5) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
context 'with the grid belonging to someone else' do
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
let(:path) { api_v3_paths.grid(other_my_page_grid.id) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
end
describe '#patch' do
let(:path) { api_v3_paths.grid(my_page_grid.id) }
let(:params) do
{
"rowCount": 10,
"name": 'foo',
"columnCount": 15,
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}]
}.with_indifferent_access
end
let(:stored_grids) do
my_page_grid
end
before do
stored_grids
patch path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'returns the altered grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('foo'.to_json)
.at_path('name')
expect(subject.body)
.to be_json_eql(params['rowCount'].to_json)
.at_path('rowCount')
expect(subject.body)
.to be_json_eql(params['widgets'][0]['identifier'].to_json)
.at_path('widgets/0/identifier')
end
it 'perists the changes' do
expect(my_page_grid.reload.row_count)
.to eql params['rowCount']
end
context 'with invalid params' do
let(:params) do
{
"rowCount": -5,
"columnCount": 15,
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}]
}.with_indifferent_access
end
it 'responds with 422 and mentions the error' do
expect(subject.status).to eq 422
expect(subject.body)
.to be_json_eql('Error'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql("Widgets is outside of the grid.".to_json)
.at_path('_embedded/errors/0/message')
expect(subject.body)
.to be_json_eql("Number of rows must be greater than 0.".to_json)
.at_path('_embedded/errors/1/message')
end
it 'does not persist the changes to widgets' do
expect(my_page_grid.reload.widgets.count)
.to eql MyPage::GridRegistration.defaults[:widgets].size
end
end
context 'with a scope param' do
let(:params) do
{
"_links": {
"scope": {
"href": ''
}
}
}.with_indifferent_access
end
it 'responds with 422 and mentions the error' do
expect(subject.status).to eq 422
expect(subject.body)
.to be_json_eql('Error'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql("You must not write a read-only attribute.".to_json)
.at_path('message')
expect(subject.body)
.to be_json_eql("scope".to_json)
.at_path('_embedded/details/attribute')
end
end
context 'with the page not existing' do
let(:path) { api_v3_paths.grid(5) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
context 'with the grid belonging to someone else' do
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
let(:path) { api_v3_paths.grid(other_my_page_grid.id) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
end
end
end
describe '#post' do
let(:path) { api_v3_paths.grids }
let(:params) do
{
"rowCount": 10,
"columnCount": 15,
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}],
"_links": {
"scope": {
"href": my_page_path
}
}
}.with_indifferent_access
end
before do
post path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'responds with 201 CREATED' do
expect(subject.status).to eq(201)
end
it 'returns the created grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql(params['rowCount'].to_json)
.at_path('rowCount')
expect(subject.body)
.to be_json_eql(params['widgets'][0]['identifier'].to_json)
.at_path('widgets/0/identifier')
end
it 'persists the grid' do
expect(Grids::Grid.count)
.to eql(1)
end
context 'with invalid params' do
let(:params) do
{
"rowCount": -5,
"columnCount": "sdjfksdfsdfdsf",
"widgets": [{
"identifier": "work_packages_assigned",
"startRow": 4,
"endRow": 8,
"startColumn": 2,
"endColumn": 5
}],
"_links": {
"scope": {
"href": my_page_path
}
}
}.with_indifferent_access
end
it 'responds with 422' do
expect(subject.status).to eq(422)
end
it 'does not create a grid' do
expect(Grids::Grid.count)
.to eql(0)
end
it 'returns the errors' do
expect(subject.body)
.to be_json_eql('Error'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql("Widgets is outside of the grid.".to_json)
.at_path('_embedded/errors/0/message')
expect(subject.body)
.to be_json_eql("Number of rows must be greater than 0.".to_json)
.at_path('_embedded/errors/1/message')
expect(subject.body)
.to be_json_eql("Number of columns must be greater than 0.".to_json)
.at_path('_embedded/errors/2/message')
end
end
end
end

@ -0,0 +1,174 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'rack/test'
describe "PATCH /api/v3/grids/:id/form", type: :request, content_type: :json do
include Rack::Test::Methods
include API::V3::Utilities::PathHelper
shared_let(:current_user) do
FactoryBot.create(:user)
end
let(:grid) do
FactoryBot.create(:my_page, user: current_user)
end
let(:path) { api_v3_paths.grid_form(grid.id) }
let(:params) { {} }
subject(:response) { last_response }
before do
login_as(current_user)
end
describe '#post' do
before do
post path, params.to_json, 'CONTENT_TYPE' => 'application/json'
end
it 'returns 200 OK' do
expect(subject.status)
.to eql 200
end
it 'is of type form' do
expect(subject.body)
.to be_json_eql("Form".to_json)
.at_path('_type')
end
it 'contains a Schema disallowing setting scope' do
expect(subject.body)
.to be_json_eql("Schema".to_json)
.at_path('_embedded/schema/_type')
expect(subject.body)
.to be_json_eql(false.to_json)
.at_path('_embedded/schema/scope/writable')
end
it 'contains the current data in the payload' do
expected = {
rowCount: 7,
columnCount: 4,
options: {},
widgets: [
{
"_type": "GridWidget",
identifier: 'work_packages_assigned',
options: {},
startRow: 1,
endRow: 7,
startColumn: 1,
endColumn: 3
},
{
"_type": "GridWidget",
identifier: 'work_packages_created',
options: {},
startRow: 1,
endRow: 7,
startColumn: 3,
endColumn: 5
}
],
"_links": {
"scope": {
"href": "/my/page",
"type": "text/html"
}
}
}
expect(subject.body)
.to be_json_eql(expected.to_json)
.at_path('_embedded/payload')
end
it 'has a commit link' do
expect(subject.body)
.to be_json_eql(api_v3_paths.grid(grid.id).to_json)
.at_path('_links/commit/href')
end
context 'with some value for the scope value' do
let(:params) do
{
'_links': {
'scope': {
'href': '/some/path'
}
}
}
end
it 'has a validation error on scope as the value is not writeable' do
expect(subject.body)
.to be_json_eql("You must not write a read-only attribute.".to_json)
.at_path('_embedded/validationErrors/scope/message')
end
end
context 'with an unsupported widget identifier' do
let(:params) do
{
"widgets": [
{
"_type": "GridWidget",
"identifier": "bogus_identifier",
"startRow": 4,
"endRow": 5,
"startColumn": 1,
"endColumn": 2
}
]
}
end
it 'has a validationError on widget' do
expect(subject.body)
.to be_json_eql("Widgets is not set to one of the allowed values.".to_json)
.at_path('_embedded/validationErrors/widgets/message')
end
end
context 'for another user\'s grid' do
let(:other_user) { FactoryBot.create(:user) }
let(:other_grid) { FactoryBot.create(:my_page, user: other_user) }
let(:path) { api_v3_paths.grid_form(other_grid.id) }
it 'returns 404 NOT FOUND' do
expect(subject.status)
.to eql 404
end
end
end
end
Loading…
Cancel
Save