diff --git a/.env.example b/.env.example index 3f56d772c..a5a5f830b 100644 --- a/.env.example +++ b/.env.example @@ -10,12 +10,12 @@ # database setup: -DATABASE_ADAPTER=postgres DATABASE_USER=postgres -DATABASE_HOST=localhost +DATABASE_NAME=timeoverflow_development # host part of the url for mail links: MAIL_LINK_HOST=localhost:3000 +MAIL_LINK_PROTO=http # a list of emails for superadmin users ADMINS="admin@timeoverflow.org" diff --git a/Gemfile b/Gemfile index e386d1131..f114372eb 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'rails-i18n' gem "rdiscount" gem 'activeadmin', '~> 1.2.1' gem 'has_scope' -gem 'pundit' +gem 'pundit', '~> 2.0.0' gem 'pg', '0.17.1' gem 'hstore_translate' gem 'dalli' @@ -48,7 +48,7 @@ group :development do gem 'airbrussh', require: false gem 'localeapp', '2.1.1', require: false gem 'letter_opener', '1.4.1' - gem 'dotenv-rails', '1.0.2' + gem 'dotenv-rails', '2.5.0' end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index ffea63e66..0541c320a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -126,9 +126,10 @@ GEM diff-lcs (1.3) domain_name (0.5.20170223) unf (>= 0.0.5, < 1.0.0) - dotenv (1.0.2) - dotenv-rails (1.0.2) - dotenv (= 1.0.2) + dotenv (2.5.0) + dotenv-rails (2.5.0) + dotenv (= 2.5.0) + railties (>= 3.2, < 6.0) elasticsearch (1.0.8) elasticsearch-api (= 1.0.7) elasticsearch-transport (= 1.0.7) @@ -152,7 +153,7 @@ GEM i18n (~> 0.5) faraday (0.9.1) multipart-post (>= 1.2, < 3) - ffi (1.9.23) + ffi (1.9.25) formtastic (3.1.5) actionpack (>= 3.2.13) formtastic_i18n (0.6.0) @@ -237,7 +238,7 @@ GEM prawn-table (0.2.2) prawn (>= 1.3.0, < 3.0.0) public_suffix (2.0.5) - pundit (0.3.0) + pundit (2.0.0) activesupport (>= 3.0.0) rack (1.6.10) rack-protection (2.0.1) @@ -321,7 +322,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.9.0) - rubyzip (1.2.1) + rubyzip (1.2.2) rufus-scheduler (3.4.2) et-orbi (~> 1.0) sass (3.4.21) @@ -412,7 +413,7 @@ DEPENDENCIES dalli database_cleaner (= 1.6.2) devise (~> 4.4.1) - dotenv-rails (= 1.0.2) + dotenv-rails (= 2.5.0) elasticsearch-model elasticsearch-rails fabrication @@ -427,7 +428,7 @@ DEPENDENCIES pg (= 0.17.1) prawn (~> 2.2.0) prawn-table (~> 0.2.2) - pundit + pundit (~> 2.0.0) rails (~> 4.2) rails-i18n rails_12factor (= 0.0.3) diff --git a/app/admin/organization.rb b/app/admin/organization.rb index 8d7f4a94e..873ec46fc 100644 --- a/app/admin/organization.rb +++ b/app/admin/organization.rb @@ -27,6 +27,19 @@ f.actions end + controller do + def destroy + resource.destroy + + if resource == current_organization + sign_out(current_user) + redirect_to root_path + else + redirect_to admin_organizations_path + end + end + end + filter :name filter :city, as: :select, collection: -> { Organization.pluck(:city).uniq } filter :neighborhood diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index 3bd9abf9b..a49ee91c4 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -1,12 +1,14 @@ class OrganizationsController < ApplicationController - before_filter :load_resource, only: [:show, :edit, :update, :destroy, :set_current] + before_filter :load_resource, only: [:show, :edit, :update, :set_current] def new @organization = Organization.new + + authorize @organization end def index - @organizations = Organization.all + @organizations = Organization.all.page(params[:page]).per(25) end def show @@ -21,8 +23,10 @@ def show def create @organization = Organization.new(organization_params) + authorize @organization + if @organization.save - redirect_to @organization, status: :created + redirect_to @organization else render action: :new, status: :unprocessable_entity end @@ -36,11 +40,6 @@ def update end end - def destroy - @organization.destroy - redirect_to organizations_path, notice: "deleted" - end - # POST /organizations/:organization_id/set_current # def set_current @@ -54,6 +53,8 @@ def set_current def load_resource @organization = Organization.find(params[:id]) + + authorize @organization end def organization_params diff --git a/app/inputs/collection_select2_input.rb b/app/inputs/collection_select2_input.rb deleted file mode 100644 index 2bcea6f65..000000000 --- a/app/inputs/collection_select2_input.rb +++ /dev/null @@ -1,5 +0,0 @@ -class CollectionSelect2Input < SimpleForm::Inputs::CollectionSelectInput - def input_html_classes - super.push("select2") - end -end diff --git a/app/inputs/datepicker_input.rb b/app/inputs/datepicker_input.rb deleted file mode 100644 index a1a1d31cd..000000000 --- a/app/inputs/datepicker_input.rb +++ /dev/null @@ -1,6 +0,0 @@ -class DatepickerInput < SimpleForm::Inputs::Base - def input - @builder.text_field(attribute_name, input_html_options) + \ - @builder.hidden_field(attribute_name, class: attribute_name.to_s + "-alt") - end -end diff --git a/app/jobs/create_push_notifications_job.rb b/app/jobs/create_push_notifications_job.rb index 3e7ccff3b..64a7ab5a6 100644 --- a/app/jobs/create_push_notifications_job.rb +++ b/app/jobs/create_push_notifications_job.rb @@ -6,6 +6,10 @@ def perform(event_id:) raise 'A valid Event must be provided' unless event - ::PushNotifications::Creator.new(event: event).create! + if event.post_id + ::PushNotifications::Creator::Post.new(event: event).create! + else + raise "You need to define a PushNotifications::Creator class for this event type ##{event.id}" + end end end diff --git a/app/jobs/send_push_notifications_job.rb b/app/jobs/send_push_notifications_job.rb new file mode 100644 index 000000000..5bf79b546 --- /dev/null +++ b/app/jobs/send_push_notifications_job.rb @@ -0,0 +1,11 @@ +class SendPushNotificationsJob < ActiveJob::Base + queue_as :cron + + def perform + push_notifications = PushNotification.where(processed_at: nil).limit(100) + + ::PushNotifications::Broadcast.new( + push_notifications: push_notifications + ).send_notifications + end +end diff --git a/app/models/event.rb b/app/models/event.rb index 2058045b7..df6275dd7 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -4,6 +4,7 @@ class Event < ActiveRecord::Base belongs_to :post belongs_to :member belongs_to :transfer + has_many :push_notifications, dependent: :destroy validates :action, presence: true validate :resource_presence diff --git a/app/models/member.rb b/app/models/member.rb index ed20524a1..7a0b575bf 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -8,6 +8,7 @@ class Member < ActiveRecord::Base belongs_to :organization has_one :account, as: :accountable has_many :movements, through: :account + has_many :events, dependent: :destroy delegate :balance, to: :account, prefix: true, allow_nil: true delegate :gender, :date_of_birth, to: :user, prefix: true, allow_nil: true diff --git a/app/models/organization.rb b/app/models/organization.rb index 9f664d9b6..4040d35a2 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -1,15 +1,15 @@ class Organization < ActiveRecord::Base has_many :members, dependent: :destroy has_many :users, -> { order "members.created_at DESC" }, through: :members - has_many :all_accounts, class_name: "Account", inverse_of: :organization + has_many :all_accounts, class_name: "Account", inverse_of: :organization, dependent: :destroy has_many :all_movements, class_name: "Movement", through: :all_accounts, source: :movements has_many :all_transfers, class_name: "Transfer", through: :all_movements, source: :transfer - has_one :account, as: :accountable + has_one :account, as: :accountable, dependent: :destroy has_many :member_accounts, through: :members, source: :account - has_many :posts + has_many :posts, dependent: :destroy has_many :offers has_many :inquiries - has_many :documents, as: :documentable + has_many :documents, as: :documentable, dependent: :destroy validates :name, presence: true, uniqueness: true diff --git a/app/models/post.rb b/app/models/post.rb index 89b91ff25..767551389 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -39,13 +39,12 @@ def self.inherited(child) attr_reader :member_id belongs_to :category - belongs_to :user belongs_to :organization belongs_to :publisher, class_name: "User", foreign_key: "publisher_id" - has_many :user_members, class_name: "Member", through: :user, source: :members has_many :transfers has_many :movements, through: :transfers + has_many :events, dependent: :destroy delegate :name, to: :category, prefix: true, allow_nil: true diff --git a/app/models/push_notification.rb b/app/models/push_notification.rb index 5aff8621a..3a61d845c 100644 --- a/app/models/push_notification.rb +++ b/app/models/push_notification.rb @@ -2,5 +2,7 @@ class PushNotification < ActiveRecord::Base belongs_to :event, foreign_key: 'event_id' belongs_to :device_token, foreign_key: 'device_token_id' - validates :event, :device_token, presence: true + validates :event, :device_token, :title, presence: true + + delegate :token, to: :device_token end diff --git a/app/models/transfer.rb b/app/models/transfer.rb index a5b28e657..b841cce45 100644 --- a/app/models/transfer.rb +++ b/app/models/transfer.rb @@ -14,8 +14,8 @@ class Transfer < ActiveRecord::Base attr_accessor :source, :destination, :amount, :hours, :minutes belongs_to :post - belongs_to :operator, class_name: "User" - has_many :movements + has_many :movements, dependent: :destroy + has_many :events, dependent: :destroy validate :different_source_and_destination diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index 44698d113..3590caf55 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -17,7 +17,7 @@ def index? end def show? - scope.where(id: record.id).exists? + record.class.where(id: record.id).exists? end def create? @@ -39,23 +39,4 @@ def edit? def destroy? false end - - def scope - Pundit.policy_scope!(member, record.class) - end - - class Scope - attr_reader :member, :user, :organization, :scope - - def initialize(member, scope) - @member = member - @user = member.user if member - @organization = member.organization if member - @scope = scope - end - - def resolve - scope - end - end end diff --git a/app/policies/organization_policy.rb b/app/policies/organization_policy.rb new file mode 100644 index 000000000..532fdda78 --- /dev/null +++ b/app/policies/organization_policy.rb @@ -0,0 +1,19 @@ +class OrganizationPolicy < ApplicationPolicy + alias_method :organization, :record + + def index? + true + end + + def create? + user&.superadmin? + end + + def update? + user&.superadmin? || user&.admins?(organization) + end + + def set_current? + user&.as_member_of(organization) + end +end diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index a0b6b3739..43682895f 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -1,8 +1,4 @@ class UserPolicy < ApplicationPolicy - def new? - user.admins?(organization) - end - def create? user.admins?(organization) end @@ -14,19 +10,4 @@ def update? user.admins?(organization) ) end - - class Scope < ApplicationPolicy::Scope - attr_reader :member, :user, :organization, :scope - - def initialize(user, scope) - @member = member - @user = member.user if member - @organization = member.organization if member - @scope = scope - end - - def resolve - scope - end - end end diff --git a/app/services/push_notifications/broadcast.rb b/app/services/push_notifications/broadcast.rb new file mode 100644 index 000000000..9681ea9d2 --- /dev/null +++ b/app/services/push_notifications/broadcast.rb @@ -0,0 +1,58 @@ +module PushNotifications + class Broadcast + class PostError < ::StandardError; end + + def initialize(push_notifications:) + @push_notifications = push_notifications + end + + # https://docs.expo.io/versions/latest/guides/push-notifications.html + def send_notifications + return unless push_notifications.any? + + response = client.post( + uri.request_uri, + notifications.to_json, + headers + ) + + unless response.is_a? Net::HTTPOK + raise PostError, "HTTP response: #{response.code}, #{response.body}" + end + + now = Time.now.utc + push_notifications.update_all(processed_at: now, updated_at: now) + end + + private + + attr_reader :push_notifications + + def notifications + push_notifications.map do |push_notification| + { + to: push_notification.token, + title: push_notification.title, + body: push_notification.body, + data: push_notification.data + } + end + end + + def client + https = Net::HTTP.new(uri.host, uri.port) + https.use_ssl = true + https + end + + def uri + URI('https://exp.host/--/api/v2/push/send') + end + + def headers + { + "Content-Type" => "application/json" + } + end + end +end diff --git a/app/services/push_notifications/creator.rb b/app/services/push_notifications/creator.rb deleted file mode 100644 index 9096cbdd8..000000000 --- a/app/services/push_notifications/creator.rb +++ /dev/null @@ -1,22 +0,0 @@ -module PushNotifications - class Creator - # Given an Event it will create as many PushNotification resources - # necessary as the resource associated to the Event will require. - # - # @param [Hash] event: - def initialize(event:) - @event = event - end - - def create! - event_notifier = EventNotifierFactory.new(event: event).build - event_notifier.device_tokens.each do |device_token| - PushNotification.create!(event: event, device_token: device_token) - end - end - - private - - attr_accessor :event - end -end diff --git a/app/services/push_notifications/creator/base.rb b/app/services/push_notifications/creator/base.rb new file mode 100644 index 000000000..b7fdec805 --- /dev/null +++ b/app/services/push_notifications/creator/base.rb @@ -0,0 +1,42 @@ +module PushNotifications + module Creator + class Base + # Given an Event it will create as many PushNotification resources + # necessary as the resource associated to the Event will require. + # + # @param [Hash] event: + def initialize(event:) + @event = event + end + + def create! + event_notifier = EventNotifierFactory.new(event: event).build + event_notifier.device_tokens.each do |device_token| + PushNotification.create!( + event: event, + device_token: device_token, + title: title, + body: body, + data: data + ) + end + end + + private + + attr_accessor :event + + def title + raise 'implement the private method `title`' + end + + def body + raise 'implement the private method `body`' + end + + def data + raise 'implement the private method `data`' + end + end + end +end diff --git a/app/services/push_notifications/creator/post.rb b/app/services/push_notifications/creator/post.rb new file mode 100644 index 000000000..4579aa961 --- /dev/null +++ b/app/services/push_notifications/creator/post.rb @@ -0,0 +1,25 @@ +module PushNotifications + module Creator + class Post < Base + private + + def title + event.post.title + end + + def body + event.post.description&.truncate(20) || 'No description' + end + + def data + if event.post.class.to_s == 'Offer' + { url: Rails.application.routes.url_helpers.offer_path(event.post) } + elsif event.post.class.to_s == 'Inquiry' + { url: Rails.application.routes.url_helpers.inquiry_path(event.post) } + else + {} + end + end + end + end +end diff --git a/app/services/push_notifications/expo_sender_service.rb b/app/services/push_notifications/expo_sender_service.rb deleted file mode 100644 index 61a8e528c..000000000 --- a/app/services/push_notifications/expo_sender_service.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'net/http' - -module PushNotifications - class ExpoAdaptorService - def initialize(notification, user) - @notification = notification - @user = user - end - - def run - user.device_tokens.each do |device_token| - # https://docs.expo.io/versions/latest/guides/push-notifications.html - uri = URI('https://exp.host/--/api/v2/push/send') - Net::HTTP.post_form(uri, post_data(device_token.token)) - end - end - - private - - attr_reader :notification, :user - - def post_data(token) - { - "to" => token, - "title" => notification.title, - "body" => notification.body - } - end - end -end diff --git a/app/services/push_notifications/post_broadcaster_service.rb b/app/services/push_notifications/post_broadcaster_service.rb deleted file mode 100644 index 76addb143..000000000 --- a/app/services/push_notifications/post_broadcaster_service.rb +++ /dev/null @@ -1,19 +0,0 @@ -module PushNotifications - class UsersBroadcasterService - def initialize(users) - @users = users - end - - def broadcast - notification = PostNotification.new - - users.each do |user| - ExpoSenderService.new(notification, user).run - end - end - - private - - attr_reader :users - end -end diff --git a/app/views/organizations/index.html.erb b/app/views/organizations/index.html.erb index 11c0c2fc6..6847e1905 100644 --- a/app/views/organizations/index.html.erb +++ b/app/views/organizations/index.html.erb @@ -27,20 +27,16 @@ <%= link_to org.name, org %> <%= org.users.count %> - <% if current_user.admins?(org) %> + <% if current_user&.admins?(org) %> <%= link_to edit_organization_path(org), class: 'action' do %> <%= glyph :pencil %> <%= t 'global.edit' %> <% end %> - <%= link_to organization_path(org), - data: { method: :delete }, - class: 'action' do %> - <%= glyph :trash %> - <%= t 'global.borrar' %> - <% end %> <% end %> <% end %> + +<%= paginate @organizations %> diff --git a/app/views/organizations/show.html.erb b/app/views/organizations/show.html.erb index 3793022cf..1bec6927a 100644 --- a/app/views/organizations/show.html.erb +++ b/app/views/organizations/show.html.erb @@ -100,21 +100,14 @@ <% end %> <% end %> - <% if superadmin? %> + <% if current_user&.active?(@organization) %>
  • - <%= link_to organization_path(@organization), - data: { method: :delete, confirm: t("are_you_sure") } do %> - <%= glyph :trash %> - <%= t "global.delete" %> + <%= link_to new_transfer_path(id: @organization, destination_account_id: @organization.account.id) do %> + <%= glyph :time %> + <%= t "global.give_time" %> <% end %>
  • <% end %> -
  • - <%= link_to new_transfer_path(id: @organization, destination_account_id: @organization.account.id) do %> - <%= glyph :time %> - <%= t "global.give_time" %> - <% end %> -
  • diff --git a/app/views/users/_cancel_membership_link.html.erb b/app/views/users/_cancel_membership_link.html.erb index 393bfb567..884e27bec 100644 --- a/app/views/users/_cancel_membership_link.html.erb +++ b/app/views/users/_cancel_membership_link.html.erb @@ -3,5 +3,5 @@ method: :delete, data: { confirm: t('users.user_rows.cancel_warning', user: member.username) } do %> <%= glyph :ban_circle %> - <%= t('global.cancel_member') %> + <%= t('global.cancel_membership') %> <% end %> \ No newline at end of file diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 38250ae2f..a25bfbf59 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -374,14 +374,13 @@ ca: pages: about: app-mobile: Aplicació Mòbil - app-mobile-text: L'aplicació mòbil de TimeOverflow està disponible per Android i IOS
    Aquesta app ha estat possible gràcies a la col·laboració de l'Ajuntament de Barcelona, a través del programa %{impulsem_link} (Barcelona Activa) 2017-2018 + app-mobile-text: L'aplicació mòbil de TimeOverflow està disponible
    Aquesta app ha estat possible gràcies a la col·laboració de l'Ajuntament de Barcelona, a través del programa %{impulsem_link} (Barcelona Activa) 2017-2018 banner-button: Sol·licita accés banner-subtitle: Us ajudarem a posar-lo en marxa o fer-vos una demostració banner-title: Ets un Banc de Temps? donate-link: Dona 1€ al mes donate-text: Amb la finalitat de donar suport a moltes comunitats la associació ADBdT ofereix la plataforma TimeOverflow a tots los Bancs de Temps. Considera %{donate_link} per contribuir a les despeses de manteniment i innovació. donate-title: Participa amb una donació - donate_link: donar 1€ al mes empower-adbdt: ADBdT empower-adbdt-title: Associació pel Desenvolupament dels Bancs de Temps empower-coopdevs: CoopDevs @@ -499,9 +498,7 @@ ca: edit: edit_user: Canviar usuari form: - admin_warning: Atenció!!! Estàs atorgant poders a aquest usuari!! notifications: Notificacions - superadmin_warning: Atenció!!! Estàs atorgant PODERS DIVINS a aquest usuari!! give_time: give_time: Donar Temps a index: @@ -515,6 +512,9 @@ ca: manage_warning_angular: Vas a canviar els permisos de l'usuari {{username}} members: Membres user_created: Usuari %{uid} %{name} guardat + member_card: + active_ago: + no_activity: new: cancel: Cancel·lar create_more_users_button: Crear i introduir un nou usuari diff --git a/config/locales/en.yml b/config/locales/en.yml index c69407497..8c5b4361b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -403,7 +403,7 @@ en: title: The software designed by and for title2: Time Banks app-mobile: Mobile App - app-mobile-text: The mobile app TimeOverflow for Android and iOS is available for download.
    This app has been possible thanks to the collaboration of the municipality of Barcelona, program %{impulsem_link} (Barcelona Activa) 2017-2018 + app-mobile-text: The mobile app TimeOverflow is available.
    This app has been possible thanks to the collaboration of the municipality of Barcelona, program %{impulsem_link} (Barcelona Activa) 2017-2018 impulsem-link: "Impulsem el que fas" posts: show: diff --git a/config/locales/es.yml b/config/locales/es.yml index b257d99ba..ec0f68f76 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -374,14 +374,13 @@ es: pages: about: app-mobile: App Móvil - app-mobile-text: La app móvil de TimeOverflow está disponible para Android y IOS
    Esta app ha sido posible gracias a la colaboración del Ayuntamiento de Barcelona, a través del programa %{impulsem_link} (Barcelona Activa) 2017-2018 + app-mobile-text: La app móvil de TimeOverflow está disponible
    Esta app ha sido posible gracias a la colaboración del Ayuntamiento de Barcelona, a través del programa %{impulsem_link} (Barcelona Activa) 2017-2018 banner-button: Solicita acceso banner-subtitle: Os ayudaremos a ponerlo en marcha o a haceros una demostración banner-title: "¿Eres un Banco de Tiempo?" donate-link: Donar 1€ al mes donate-text: Con el fin de apoyar a muchas comunidades la asociación ADBdT ofrece la plataforma TimeOverflow a todos los Bancos de Tiempo. Considera %{donate_link} para contribuir a los gastos de mantenimiento e innovación. donate-title: Participa con una donación - donate_link: donar 1€ al mes empower-adbdt: ADBdT empower-adbdt-title: Asociación para el Desarrollo de los Bancos de Tiempo empower-coopdevs: CoopDevs @@ -499,9 +498,7 @@ es: edit: edit_user: Cambiar usuario form: - admin_warning: Atención!!! Estás dando poderes a este usuario!! notifications: Notificaciones - superadmin_warning: Atención!!! Estás dando PODERES DIVINOS a este usuario!! give_time: give_time: Dar Tiempo a index: @@ -515,6 +512,9 @@ es: manage_warning_angular: Va a cambiar los privilegios del usuario {{username}} members: Miembros user_created: Usuario %{uid} %{name} guardado + member_card: + active_ago: + no_activity: new: cancel: Cancelar create_more_users_button: Crear e introducir otro usuario diff --git a/config/locales/eu.yml b/config/locales/eu.yml index cdb099c0d..ee6060db4 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -387,7 +387,6 @@ eu: donate-link: donate-text: komunitate askori babesteko asmotan ADBdt elkarteak , TimeOverflow plataforma eskaintzen die Denbora Banku guztiei. Hausnar ezazu %{donate_link} mantenu eta berritze lanetan laguntzeko. donate-title: Laguntzan parte hartu - donate_link: Hilean €1 ematea empower-adbdt: ADBdt empower-adbdt-title: Denbora Bankuen garapenerako elkartea empower-coopdevs: CoopDevs @@ -505,9 +504,7 @@ eu: edit: edit_user: Erabiltzailea aldatu form: - admin_warning: Kontuz!!! Erabiltzaile honi boterea ematen ari zara. notifications: Jakinarazpen - superadmin_warning: Kontuz!!! Erabiltzaile honi izugarrizko boterea ematen ari zara. give_time: give_time: honi denbora eman index: @@ -521,6 +518,9 @@ eu: manage_warning_angular: " {{username}} erabiltzailearen onurak, aldatuko diztuzu" members: user_created: "%{uid} %{name} erabiltzailea gorde da" + member_card: + active_ago: + no_activity: new: cancel: Deuseztatu create_more_users_button: Beste erabiltzaile bat sortu eta sartu diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index fba1b09f6..a1b31f8c6 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -381,7 +381,6 @@ pt-BR: donate-link: donate-text: donate-title: - donate_link: empower-adbdt: ADBdt empower-adbdt-title: Associação para o Desenvolvimento dos Bancos de Tempo empower-coopdevs: CoopDevs @@ -499,9 +498,7 @@ pt-BR: edit: edit_user: Trocar usuário form: - admin_warning: Atenção!!! Você está dando poderes a este usuário!! notifications: Notificações - superadmin_warning: Atenção!!! Você está dando PODERES DIVINOS a este usuário!! give_time: give_time: Dar Tempo a index: @@ -515,6 +512,9 @@ pt-BR: manage_warning_angular: Mudará os privilégios do usuário {{username}} members: user_created: Usuário %{uid} %{name} guardado + member_card: + active_ago: + no_activity: new: cancel: Cancelar create_more_users_button: Criar e introduzir outro usuário diff --git a/config/newrelic.yml b/config/newrelic.yml deleted file mode 100644 index 6f86fde59..000000000 --- a/config/newrelic.yml +++ /dev/null @@ -1,255 +0,0 @@ -# Here are the settings that are common to all environments -common: &default_settings - # ============================== LICENSE KEY =============================== - - # You must specify the license key associated with your New Relic - # account. This key binds your Agent's data to your account in the - # New Relic service. - license_key: '<%= ENV["NEW_RELIC_LICENSE_KEY"] %>' - - # Agent Enabled (Rails Only) - # Use this setting to force the agent to run or not run. - # Default is 'auto' which means the agent will install and run only - # if a valid dispatcher such as Mongrel is running. This prevents - # it from running with Rake or the console. Set to false to - # completely turn the agent off regardless of the other settings. - # Valid values are true, false and auto. - # - # agent_enabled: auto - - # Application Name Set this to be the name of your application as - # you'd like it show up in New Relic. The service will then auto-map - # instances of your application into an "application" on your - # dashboard page. If you want to map this instance into multiple - # apps, like "AJAX Requests" and "All UI" then specify a semicolon - # separated list of up to three distinct names, or a yaml list. - # Defaults to the capitalized RAILS_ENV or RACK_ENV (i.e., - # Production, Staging, etc) - # - # Example: - # - # app_name: - # - Ajax Service - # - All Services - # - app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> - - # When "true", the agent collects performance data about your - # application and reports this data to the New Relic service at - # newrelic.com. This global switch is normally overridden for each - # environment below. (formerly called 'enabled') - monitor_mode: true - - # Developer mode should be off in every environment but - # development as it has very high overhead in memory. - developer_mode: false - - # The newrelic agent generates its own log file to keep its logging - # information separate from that of your application. Specify its - # log level here. - log_level: info - - # Optionally set the path to the log file This is expanded from the - # root directory (may be relative or absolute, e.g. 'log/' or - # '/var/log/') The agent will attempt to create this directory if it - # does not exist. - # log_file_path: 'log' - - # Optionally set the name of the log file, defaults to 'newrelic_agent.log' - # log_file_name: 'newrelic_agent.log' - - # The newrelic agent communicates with the service via http by - # default. If you want to communicate via https to increase - # security, then turn on SSL by setting this value to true. Note, - # this will result in increased CPU overhead to perform the - # encryption involved in SSL communication, but this work is done - # asynchronously to the threads that process your application code, - # so it should not impact response times. - ssl: false - - # EXPERIMENTAL: enable verification of the SSL certificate sent by - # the server. This setting has no effect unless SSL is enabled - # above. This may block your application. Only enable it if the data - # you send us needs end-to-end verified certificates. - # - # This means we cannot cache the DNS lookup, so each request to the - # service will perform a lookup. It also means that we cannot - # use a non-blocking lookup, so in a worst case, if you have DNS - # problems, your app may block indefinitely. - # verify_certificate: true - - # Set your application's Apdex threshold value with the 'apdex_t' - # setting, in seconds. The apdex_t value determines the buckets used - # to compute your overall Apdex score. - # Requests that take less than apdex_t seconds to process will be - # classified as Satisfying transactions; more than apdex_t seconds - # as Tolerating transactions; and more than four times the apdex_t - # value as Frustrating transactions. - # For more about the Apdex standard, see - # http://newrelic.com/docs/general/apdex - - apdex_t: 0.5 - - #============================== Browser Monitoring =============================== - # New Relic Real User Monitoring gives you insight into the performance real users are - # experiencing with your website. This is accomplished by measuring the time it takes for - # your users' browsers to download and render your web pages by injecting a small amount - # of JavaScript code into the header and footer of each page. - browser_monitoring: - # By default the agent automatically injects the monitoring JavaScript - # into web pages. Set this attribute to false to turn off this behavior. - auto_instrument: true - - # Proxy settings for connecting to the service. - # - # If a proxy is used, the host setting is required. Other settings - # are optional. Default port is 8080. - # - # proxy_host: hostname - # proxy_port: 8080 - # proxy_user: - # proxy_pass: - - - # Tells transaction tracer and error collector (when enabled) - # whether or not to capture HTTP params. When true, frameworks can - # exclude HTTP parameters from being captured. - # Rails: the RoR filter_parameter_logging excludes parameters - # Java: create a config setting called "ignored_params" and set it to - # a comma separated list of HTTP parameter names. - # ex: ignored_params: credit_card, ssn, password - capture_params: false - - - # Transaction tracer captures deep information about slow - # transactions and sends this to the service once a - # minute. Included in the transaction is the exact call sequence of - # the transactions including any SQL statements issued. - transaction_tracer: - - # Transaction tracer is enabled by default. Set this to false to - # turn it off. This feature is only available at the Professional - # and above product levels. - enabled: true - - # Threshold in seconds for when to collect a transaction - # trace. When the response time of a controller action exceeds - # this threshold, a transaction trace will be recorded and sent to - # the service. Valid values are any float value, or (default) - # "apdex_f", which will use the threshold for an dissatisfying - # Apdex controller action - four times the Apdex T value. - transaction_threshold: apdex_f - - # When transaction tracer is on, SQL statements can optionally be - # recorded. The recorder has three modes, "off" which sends no - # SQL, "raw" which sends the SQL statement in its original form, - # and "obfuscated", which strips out numeric and string literals - record_sql: obfuscated - - # Threshold in seconds for when to collect stack trace for a SQL - # call. In other words, when SQL statements exceed this threshold, - # then capture and send the current stack trace. This is - # helpful for pinpointing where long SQL calls originate from - stack_trace_threshold: 0.500 - - # Determines whether the agent will capture query plans for slow - # SQL queries. Only supported in mysql and postgres. Should be - # set to false when using other adapters. - # explain_enabled: true - - # Threshold for query execution time below which query plans will not - # not be captured. Relevant only when `explain_enabled` is true. - # explain_threshold: 0.5 - - # Error collector captures information about uncaught exceptions and - # sends them to the service for viewing - error_collector: - - # Error collector is enabled by default. Set this to false to turn - # it off. This feature is only available at the Professional and above - # product levels - enabled: true - - # Rails Only - tells error collector whether or not to capture a - # source snippet around the place of the error when errors are View - # related. - capture_source: true - - # To stop specific errors from reporting to New Relic, set this property - # to comma separated values. Default is to ignore routing errors - # which are how 404's get triggered. - # - ignore_errors: ActionController::RoutingError - - # (Advanced) Uncomment this to ensure the cpu and memory samplers - # won't run. Useful when you are using the agent to monitor an - # external resource - # disable_samplers: true - - # If you aren't interested in visibility in these areas, you can - # disable the instrumentation to reduce overhead. - # - # disable_view_instrumentation: true - # disable_activerecord_instrumentation: true - # disable_memcache_instrumentation: true - # disable_dj: true - - # If you're interested in capturing memcache keys as though they - # were SQL uncomment this flag. Note that this does increase - # overhead slightly on every memcached call, and can have security - # implications if your memcached keys are sensitive - # capture_memcache_keys: true - - # Certain types of instrumentation such as GC stats will not work if - # you are running multi-threaded. Please let us know. - # multi_threaded = false - -# Application Environments -# ------------------------------------------ -# Environment specific settings are in this section. -# For Rails applications, RAILS_ENV is used to determine the environment -# For Java applications, pass -Dnewrelic.environment to set -# the environment - -# NOTE if your application has other named environments, you should -# provide newrelic configuration settings for these environments here. - -development: - <<: *default_settings - # Turn off communication to New Relic service in development mode (also - # 'enabled'). - # NOTE: for initial evaluation purposes, you may want to temporarily - # turn the agent on in development mode. - monitor_mode: false - - # Rails Only - when running in Developer Mode, the New Relic Agent will - # present performance information on the last 100 transactions you have - # executed since starting the mongrel. - # NOTE: There is substantial overhead when running in developer mode. - # Do not use for production or load testing. - developer_mode: true - - # Enable textmate links - # textmate: true - -test: - <<: *default_settings - # It almost never makes sense to turn on the agent when running - # unit, functional or integration tests or the like. - monitor_mode: false - -# Turn on the agent in production for 24x7 monitoring. NewRelic -# testing shows an average performance impact of < 5 ms per -# transaction, you you can leave this on all the time without -# incurring any user-visible performance degradation. -production: - <<: *default_settings - monitor_mode: true - -# Many applications have a staging environment which behaves -# identically to production. Support for that environment is provided -# here. By default, the staging environment has the agent turned on. -staging: - <<: *default_settings - monitor_mode: true - app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> (Staging) diff --git a/config/routes.rb b/config/routes.rb index e1068a592..88c55af3a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -33,7 +33,7 @@ get :give_time, on: :member end - resources :organizations, concerns: :accountable do + resources :organizations, concerns: :accountable, except: :destroy do member do post :set_current end diff --git a/config/schedule.yml b/config/schedule.yml new file mode 100644 index 000000000..56487c3a6 --- /dev/null +++ b/config/schedule.yml @@ -0,0 +1,4 @@ +send_push_notifications_job: + cron: '*/5 * * * *' + class: 'SendPushNotificationsJob' + queue: cron diff --git a/db/migrate/20180604145622_add_title_to_push_notification.rb b/db/migrate/20180604145622_add_title_to_push_notification.rb new file mode 100644 index 000000000..9b4a91ed2 --- /dev/null +++ b/db/migrate/20180604145622_add_title_to_push_notification.rb @@ -0,0 +1,9 @@ +class AddTitleToPushNotification < ActiveRecord::Migration + def up + add_column :push_notifications, :title, :string, null: false, default: '' + end + + def down + remove_column :push_notifications, :title + end +end diff --git a/db/migrate/20180828160700_add_body_to_push_notification.rb b/db/migrate/20180828160700_add_body_to_push_notification.rb new file mode 100644 index 000000000..52e697ca0 --- /dev/null +++ b/db/migrate/20180828160700_add_body_to_push_notification.rb @@ -0,0 +1,9 @@ +class AddBodyToPushNotification < ActiveRecord::Migration + def up + add_column :push_notifications, :body, :string, null: false, default: '' + end + + def down + remove_column :push_notifications, :body + end +end diff --git a/db/migrate/20180831161349_add_data_to_push_notification.rb b/db/migrate/20180831161349_add_data_to_push_notification.rb new file mode 100644 index 000000000..410349f2f --- /dev/null +++ b/db/migrate/20180831161349_add_data_to_push_notification.rb @@ -0,0 +1,9 @@ +class AddDataToPushNotification < ActiveRecord::Migration + def up + add_column :push_notifications, :data, :json, null: false, default: '{}' + end + + def down + remove_column :push_notifications, :data + end +end diff --git a/db/schema.rb b/db/schema.rb index 341743d47..fb61c5627 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180530180546) do +ActiveRecord::Schema.define(version: 20180831161349) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -159,11 +159,14 @@ add_index "posts", ["user_id"], name: "index_posts_on_user_id", using: :btree create_table "push_notifications", force: :cascade do |t| - t.integer "event_id", null: false - t.integer "device_token_id", null: false + t.integer "event_id", null: false + t.integer "device_token_id", null: false t.datetime "processed_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "title", default: "", null: false + t.string "body", default: "", null: false + t.json "data", default: {}, null: false end create_table "transfers", force: :cascade do |t| diff --git a/spec/admin/organizations_controller_spec.rb b/spec/admin/organizations_controller_spec.rb new file mode 100644 index 000000000..743051c07 --- /dev/null +++ b/spec/admin/organizations_controller_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +RSpec.describe Admin::OrganizationsController, type: :controller do + let(:organization) { Fabricate(:organization) } + let(:member) { Fabricate(:member, organization: organization) } + let(:user) { member.user } + + before do + login(user) + allow(controller).to receive(:authenticate_superuser!).and_return(true) + end + + describe "DELETE #destroy" do + it "sign out if current user is logged to organization deleted" do + expect { + delete :destroy, id: organization.id + }.to change { controller.current_user }.to nil + end + end +end \ No newline at end of file diff --git a/spec/controllers/organizations_controller_spec.rb b/spec/controllers/organizations_controller_spec.rb index 7dcbffed3..8bf15c25b 100644 --- a/spec/controllers/organizations_controller_spec.rb +++ b/spec/controllers/organizations_controller_spec.rb @@ -1,16 +1,59 @@ require 'spec_helper' RSpec.describe OrganizationsController do - let(:organization) { Fabricate(:organization) } + let!(:organization) { Fabricate(:organization) } let(:member) { Fabricate(:member, organization: organization) } let(:user) { member.user } - describe '#show' do - it 'links to new_transfer_path' do + describe 'GET #show' do + it 'displays the organization page' do get 'show', id: organization.id - expect(response.body).to include( - "" - ) + + expect(assigns(:organization)).to eq(organization) + expect(response.status).to eq(200) + expect(response.body).to include(organization.name) + end + end + + describe 'GET #index' do + it 'populates and array of organizations' do + get :index + + expect(assigns(:organizations)).to eq([organization]) + end + end + + describe 'POST #create' do + it 'only superdamins are authorized create to new organizations' do + login(member.user) + + expect { + post :create, organization: { name: 'New cool organization' } + }.not_to change { Organization.count } + end + end + + describe 'POST #update' do + context 'with a logged user (admins organization)' do + let(:member) { Fabricate(:member, organization: organization, manager: true) } + + it 'allows to update organization' do + login(member.user) + + post :update, id: organization.id, organization: { name: 'New org name' } + + organization.reload + expect(organization.name).to eq('New org name') + end + end + + context 'without a logged user' do + it 'does not allow to update organization' do + post :update, id: organization.id, organization: { name: 'New org name' } + + expect(response).to redirect_to(root_path) + expect(flash[:error]).to eq('You are not authorized to perform this action.') + end end end diff --git a/spec/fabricators/device_token_fabricator.rb b/spec/fabricators/device_token_fabricator.rb index 64f2a980f..ad354d147 100644 --- a/spec/fabricators/device_token_fabricator.rb +++ b/spec/fabricators/device_token_fabricator.rb @@ -1,2 +1,3 @@ Fabricator(:device_token) do + token 'token' end diff --git a/spec/fabricators/push_notification_fabricator.rb b/spec/fabricators/push_notification_fabricator.rb new file mode 100644 index 000000000..539639867 --- /dev/null +++ b/spec/fabricators/push_notification_fabricator.rb @@ -0,0 +1,2 @@ +Fabricator(:push_notification) do +end diff --git a/spec/jobs/create_push_notifications_job_spec.rb b/spec/jobs/create_push_notifications_job_spec.rb index 34bcc0414..0ff5a54c9 100644 --- a/spec/jobs/create_push_notifications_job_spec.rb +++ b/spec/jobs/create_push_notifications_job_spec.rb @@ -18,8 +18,8 @@ let(:event_id) { event.id } it 'calls the PushNotification creator' do - creator = instance_double(::PushNotifications::Creator) - expect(::PushNotifications::Creator).to receive(:new) + creator = instance_double(::PushNotifications::Creator::Post) + expect(::PushNotifications::Creator::Post).to receive(:new) .with(event: event) .and_return(creator) expect(creator).to receive(:create!) diff --git a/spec/jobs/send_push_notifications_job_spec.rb b/spec/jobs/send_push_notifications_job_spec.rb new file mode 100644 index 000000000..07c0fe73a --- /dev/null +++ b/spec/jobs/send_push_notifications_job_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +RSpec.describe SendPushNotificationsJob, type: :job do + describe '#perform' do + let(:user) { Fabricate(:user) } + let(:device_token) { Fabricate(:device_token, user: user) } + let(:post) { Fabricate(:post) } + let(:event_created) { Fabricate(:event, post: post, action: :created) } + let(:event_updated) { Fabricate(:event, post: post, action: :updated) } + let(:push_notification) do + Fabricate( + :push_notification, + event: event_created, + device_token: device_token, + title: 'A new Post hase been created.' + ) + end + let(:processed_push_notification) do + Fabricate( + :push_notification, + event: event_updated, + device_token: device_token, + title: 'A new Post hase been created.', + processed_at: Time.zone.now + ) + end + + before do + push_notification + processed_push_notification + end + + it 'calls Broadcast to send the notifications' do + broadcast = instance_double(::PushNotifications::Broadcast) + expect(::PushNotifications::Broadcast).to receive(:new) + .with(push_notifications: [push_notification]) + .and_return(broadcast) + expect(broadcast).to receive(:send_notifications) + + described_class.new.perform + end + end +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 4c9b3610d..c132b1744 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -35,6 +35,7 @@ it { is_expected.to belong_to(:post) } it { is_expected.to belong_to(:member) } it { is_expected.to belong_to(:transfer) } + it { is_expected.to have_many(:push_notifications) } it { is_expected.to have_db_column(:post_id) } it { is_expected.to have_db_column(:member_id) } diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 7b124ac05..817b3d1c1 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -6,6 +6,7 @@ it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:organization) } it { is_expected.to have_one(:account) } + it { is_expected.to have_many(:events) } it { is_expected.to delegate_method(:balance).to(:account).with_prefix } it { is_expected.to delegate_method(:gender).to(:user).with_prefix } it { is_expected.to delegate_method(:date_of_birth).to(:user).with_prefix } diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb new file mode 100644 index 000000000..3dee6f67b --- /dev/null +++ b/spec/models/post_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +RSpec.describe Post do + describe 'Relations' do + it { is_expected.to belong_to(:category) } + it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:publisher) } + it { is_expected.to have_many(:transfers) } + it { is_expected.to have_many(:movements) } + it { is_expected.to have_many(:events) } + end +end diff --git a/spec/models/push_notification_spec.rb b/spec/models/push_notification_spec.rb index f17261023..7258af63b 100644 --- a/spec/models/push_notification_spec.rb +++ b/spec/models/push_notification_spec.rb @@ -4,6 +4,7 @@ describe 'Validations' do it { is_expected.to validate_presence_of(:event) } it { is_expected.to validate_presence_of(:device_token) } + it { is_expected.to validate_presence_of(:title) } end describe 'Associations' do @@ -13,4 +14,13 @@ it { is_expected.to have_db_column(:event_id) } it { is_expected.to have_db_column(:device_token_id) } end + + describe "#token" do + let(:device_token) { Fabricate.build(:device_token, token: 'token') } + let(:push_notification) { described_class.new(device_token: device_token) } + + it 'returns the associated DeviceToken\'s token' do + expect(push_notification.token).to eq('token') + end + end end diff --git a/spec/models/transfer_spec.rb b/spec/models/transfer_spec.rb new file mode 100644 index 000000000..b041ac033 --- /dev/null +++ b/spec/models/transfer_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +RSpec.describe Transfer do + describe 'Relations' do + it { is_expected.to belong_to(:post) } + it { is_expected.to have_many(:movements) } + it { is_expected.to have_many(:events) } + end +end diff --git a/spec/services/push_notifications/broadcast_spec.rb b/spec/services/push_notifications/broadcast_spec.rb new file mode 100644 index 000000000..cd16618de --- /dev/null +++ b/spec/services/push_notifications/broadcast_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +RSpec.describe PushNotifications::Broadcast do + describe '#send_notifications' do + let(:user) { Fabricate(:user) } + let(:device_token) { Fabricate(:device_token, user: user) } + let(:post) { Fabricate(:post) } + let(:event_created) { Fabricate(:event, post: post, action: :created) } + let(:push_notifications) { PushNotification.all } + let(:notification) do + { + to: push_notification.token, + title: push_notification.title, + body: push_notification.body, + data: push_notification.data + } + end + let(:uri) { URI('https://exp.host/--/api/v2/push/send') } + let(:client) { Net::HTTP.new(uri.host, uri.port) } + let(:headers) { { "Content-Type" => "application/json" } } + + context 'when there are push notifications to send' do + let(:push_notification) do + Fabricate( + :push_notification, + event: event_created, + device_token: device_token, + title: 'Hola', + body: 'Caracola', + data: {} + ) + end + + context 'when the HTTP response is OK' do + let(:http_response) { Net::HTTPOK.new(1.0, 200, "OK") } + + it 'calls Expo API to send notifications' do + allow(Net::HTTP).to receive(:new).with(uri.host, uri.port).and_return(client) + expect(client).to receive(:post).with(uri.request_uri, [notification].to_json, headers).and_return(http_response) + + described_class.new(push_notifications: push_notifications).send_notifications + end + + it 'flags the push_notification as processed' do + allow(Net::HTTP).to receive(:new).with(uri.host, uri.port).and_return(client) + allow(client).to receive(:post).with(uri.request_uri, [notification].to_json, headers).and_return(http_response) + + described_class.new(push_notifications: push_notifications).send_notifications + + expect(push_notification.reload.processed_at).to_not be_nil + end + + context 'when the HTTP response is not OK' do + let(:http_response) { Net::HTTPNotFound.new(1.0, 400, "Not Found") } + + it 'raises an error' do + allow(Net::HTTP).to receive(:new).with(uri.host, uri.port).and_return(client) + allow(client).to receive(:post).with(uri.request_uri, [notification].to_json, headers).and_return(http_response) + allow(http_response).to receive(:body).and_return("Everything is broken!") + + expect { + described_class.new(push_notifications: push_notifications).send_notifications + }.to raise_error(::PushNotifications::Broadcast::PostError) + end + end + end + end + + context 'when there are no push notifications to send' do + it 'calls Expo API to send notifications' do + expect(Net::HTTP).to_not receive(:new).with(uri.host, uri.port) + + described_class.new(push_notifications: push_notifications).send_notifications + end + end + end +end diff --git a/spec/services/push_notifications/creator_spec.rb b/spec/services/push_notifications/post_spec.rb similarity index 74% rename from spec/services/push_notifications/creator_spec.rb rename to spec/services/push_notifications/post_spec.rb index 4b8df3f17..43d2b6165 100644 --- a/spec/services/push_notifications/creator_spec.rb +++ b/spec/services/push_notifications/post_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe PushNotifications::Creator do +RSpec.describe PushNotifications::Creator::Post do let(:user) { Fabricate(:user) } let!(:device_token) { Fabricate(:device_token, user: user, token: 'aloha') } let(:organization) { Fabricate(:organization) } @@ -14,12 +14,9 @@ describe '#create!' do it 'creates as many PushNotification resources as needed' do - expect(PushNotification).to receive(:create!).with( - event: event, - device_token: device_token - ).once - - creator.create! + expect { + creator.create! + }.to change{PushNotification.count}.by(1) end end end diff --git a/spec/views/organizations/show.html.erb_spec.rb b/spec/views/organizations/show.html.erb_spec.rb new file mode 100644 index 000000000..785cb9ca2 --- /dev/null +++ b/spec/views/organizations/show.html.erb_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +RSpec.describe 'organizations/show' do + let(:organization) { Fabricate(:organization) } + + before do + allow(view).to receive(:admin?).and_return(false) + allow(view).to receive(:superadmin?).and_return(false) + + assign :organization, organization + end + + context 'without a logged user' do + before do + allow(view).to receive(:current_user).and_return(nil) + + assign :movements, [] + render template: 'organizations/show' + end + + it 'does not display link to new_transfer_path' do + expect(rendered).not_to have_link( + t('global.give_time'), + href: new_transfer_path( + id: organization, + destination_account_id: organization.account.id + ) + ) + end + + it 'does not display the movements section' do + expect(rendered).not_to match t('organizations.movements') + end + end + + context 'with a logged user (organization member)' do + let(:member) { Fabricate(:member, organization: organization) } + let(:user) { member.user } + + before do + allow(view).to receive(:current_user).and_return(user) + + assign :movements, Movement.page + render template: 'organizations/show' + end + + it 'links to new_transfer_path' do + expect(rendered).to have_link( + t('global.give_time'), + href: new_transfer_path( + id: organization, + destination_account_id: organization.account.id + ) + ) + end + + it 'diplays the movements section' do + expect(rendered).to match t('shared.movements.movements') + end + end +end