mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
split my_page module off of grids
This commit is contained in:
@@ -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
|
||||
+2
-2
@@ -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
|
||||
Reference in New Issue
Block a user