diff --git a/app/controllers/account/entropies_controller.rb b/app/controllers/account/entropies_controller.rb index 9876e99d8..7290c1e4e 100644 --- a/app/controllers/account/entropies_controller.rb +++ b/app/controllers/account/entropies_controller.rb @@ -3,7 +3,11 @@ class Account::EntropiesController < ApplicationController def update Current.account.entropy.update!(entropy_params) - redirect_to account_settings_path, notice: "Account updated" + + respond_to do |format| + format.html { redirect_to account_settings_path, notice: "Account updated" } + format.json { head :no_content } + end end private diff --git a/app/controllers/account/exports_controller.rb b/app/controllers/account/exports_controller.rb index dbe0a11c1..32fb5bd42 100644 --- a/app/controllers/account/exports_controller.rb +++ b/app/controllers/account/exports_controller.rb @@ -6,11 +6,20 @@ class Account::ExportsController < ApplicationController CURRENT_EXPORT_LIMIT = 10 def show + respond_to do |format| + format.html + format.json { @export ? render(:show) : head(:not_found) } + end end def create - Current.account.exports.create!(user: Current.user).build_later - redirect_to account_settings_path, notice: "Export started. You'll receive an email when it's ready." + @export = Current.account.exports.create!(user: Current.user) + @export.build_later + + respond_to do |format| + format.html { redirect_to account_settings_path, notice: "Export started. You'll receive an email when it's ready." } + format.json { render :show, status: :created } + end end private @@ -23,6 +32,7 @@ class Account::ExportsController < ApplicationController end def set_export - @export = Current.account.exports.completed.find_by(id: params[:id], user: Current.user) + scope = request.format.json? ? Current.account.exports : Current.account.exports.completed + @export = scope.find_by(id: params[:id], user: Current.user) end end diff --git a/app/controllers/account/join_codes_controller.rb b/app/controllers/account/join_codes_controller.rb index 217ee603c..3df9552db 100644 --- a/app/controllers/account/join_codes_controller.rb +++ b/app/controllers/account/join_codes_controller.rb @@ -1,4 +1,6 @@ class Account::JoinCodesController < ApplicationController + wrap_parameters :account_join_code + before_action :set_join_code before_action :ensure_admin, only: %i[ update destroy ] @@ -10,15 +12,25 @@ class Account::JoinCodesController < ApplicationController def update if @join_code.update(join_code_params) - redirect_to account_join_code_path + respond_to do |format| + format.html { redirect_to account_join_code_path } + format.json { head :no_content } + end else - render :edit, status: :unprocessable_entity + respond_to do |format| + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @join_code.errors, status: :unprocessable_entity } + end end end def destroy @join_code.reset - redirect_to account_join_code_path + + respond_to do |format| + format.html { redirect_to account_join_code_path } + format.json { head :no_content } + end end private diff --git a/app/controllers/account/settings_controller.rb b/app/controllers/account/settings_controller.rb index 27f47ab99..6201ca656 100644 --- a/app/controllers/account/settings_controller.rb +++ b/app/controllers/account/settings_controller.rb @@ -1,4 +1,6 @@ class Account::SettingsController < ApplicationController + wrap_parameters :account + before_action :ensure_admin, only: :update before_action :set_account @@ -8,7 +10,11 @@ class Account::SettingsController < ApplicationController def update @account.update!(account_params) - redirect_to account_settings_path + + respond_to do |format| + format.html { redirect_to account_settings_path } + format.json { head :no_content } + end end private diff --git a/app/controllers/boards/columns_controller.rb b/app/controllers/boards/columns_controller.rb index 4b71e7f3e..a3cf867ea 100644 --- a/app/controllers/boards/columns_controller.rb +++ b/app/controllers/boards/columns_controller.rb @@ -18,7 +18,7 @@ class Boards::ColumnsController < ApplicationController respond_to do |format| format.turbo_stream - format.json { head :created, location: board_column_path(@board, @column, format: :json) } + format.json { render :show, status: :created, location: board_column_path(@board, @column, format: :json) } end end diff --git a/app/controllers/boards/entropies_controller.rb b/app/controllers/boards/entropies_controller.rb index e42631eaf..557adb2d9 100644 --- a/app/controllers/boards/entropies_controller.rb +++ b/app/controllers/boards/entropies_controller.rb @@ -1,10 +1,17 @@ class Boards::EntropiesController < ApplicationController + wrap_parameters :board + include BoardScoped before_action :ensure_permission_to_admin_board def update @board.update!(entropy_params) + + respond_to do |format| + format.turbo_stream + format.json { head :no_content } + end end private diff --git a/app/controllers/boards/involvements_controller.rb b/app/controllers/boards/involvements_controller.rb index a904f2989..271b50262 100644 --- a/app/controllers/boards/involvements_controller.rb +++ b/app/controllers/boards/involvements_controller.rb @@ -3,5 +3,11 @@ class Boards::InvolvementsController < ApplicationController def update @board.access_for(Current.user).update!(involvement: params[:involvement]) + + respond_to do |format| + format.html + format.turbo_stream + format.json { head :no_content } + end end end diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 604938deb..1e93a5758 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -26,7 +26,7 @@ class BoardsController < ApplicationController respond_to do |format| format.html { redirect_to board_path(@board) } - format.json { head :created, location: board_path(@board, format: :json) } + format.json { render :show, status: :created, location: board_path(@board, format: :json) } end end diff --git a/app/controllers/cards/comments/reactions_controller.rb b/app/controllers/cards/comments/reactions_controller.rb index 0c822360e..32ca9f2f8 100644 --- a/app/controllers/cards/comments/reactions_controller.rb +++ b/app/controllers/cards/comments/reactions_controller.rb @@ -22,7 +22,7 @@ class Cards::Comments::ReactionsController < ApplicationController respond_to do |format| format.turbo_stream { render "reactions/create" } - format.json { head :created } + format.json { render "reactions/show", status: :created } end end diff --git a/app/controllers/cards/comments_controller.rb b/app/controllers/cards/comments_controller.rb index f2b318813..6a377bc2c 100644 --- a/app/controllers/cards/comments_controller.rb +++ b/app/controllers/cards/comments_controller.rb @@ -14,7 +14,7 @@ class Cards::CommentsController < ApplicationController respond_to do |format| format.turbo_stream - format.json { head :created, location: card_comment_path(@card, @comment, format: :json) } + format.json { render :show, status: :created, location: card_comment_path(@card, @comment, format: :json) } end end diff --git a/app/controllers/cards/publishes_controller.rb b/app/controllers/cards/publishes_controller.rb index a0378eec3..a544e6979 100644 --- a/app/controllers/cards/publishes_controller.rb +++ b/app/controllers/cards/publishes_controller.rb @@ -4,11 +4,17 @@ class Cards::PublishesController < ApplicationController def create @card.publish - if add_another_param? - card = @board.cards.create!(status: :drafted) - redirect_to card_draft_path(card), notice: "Card added" - else - redirect_to @card.board + respond_to do |format| + format.html do + if add_another_param? + card = @board.cards.create!(status: :drafted) + redirect_to card_draft_path(card), notice: "Card added" + else + redirect_to @card.board + end + end + + format.json { head :created } end end diff --git a/app/controllers/cards/reactions_controller.rb b/app/controllers/cards/reactions_controller.rb index 4105a4121..4f19a1c64 100644 --- a/app/controllers/cards/reactions_controller.rb +++ b/app/controllers/cards/reactions_controller.rb @@ -21,7 +21,7 @@ class Cards::ReactionsController < ApplicationController respond_to do |format| format.turbo_stream { render "reactions/create" } - format.json { head :created } + format.json { render "reactions/show", status: :created } end end diff --git a/app/controllers/cards/readings_controller.rb b/app/controllers/cards/readings_controller.rb index 75fdbac19..4b71272f8 100644 --- a/app/controllers/cards/readings_controller.rb +++ b/app/controllers/cards/readings_controller.rb @@ -4,11 +4,21 @@ class Cards::ReadingsController < ApplicationController def create @notification = @card.read_by(Current.user) record_board_access + + respond_to do |format| + format.turbo_stream + format.json { head :created } + end end def destroy @notification = @card.unread_by(Current.user) record_board_access + + respond_to do |format| + format.turbo_stream + format.json { head :no_content } + end end private diff --git a/app/controllers/cards/steps_controller.rb b/app/controllers/cards/steps_controller.rb index 0b788fe36..387dd8e23 100644 --- a/app/controllers/cards/steps_controller.rb +++ b/app/controllers/cards/steps_controller.rb @@ -3,12 +3,16 @@ class Cards::StepsController < ApplicationController before_action :set_step, only: %i[ show edit update destroy ] + def index + fresh_when etag: @card.steps + end + def create @step = @card.steps.create!(step_params) respond_to do |format| format.turbo_stream - format.json { head :created, location: card_step_path(@card, @step, format: :json) } + format.json { render :show, status: :created, location: card_step_path(@card, @step, format: :json) } end end diff --git a/app/controllers/cards_controller.rb b/app/controllers/cards_controller.rb index e007527b8..482d2ff78 100644 --- a/app/controllers/cards_controller.rb +++ b/app/controllers/cards_controller.rb @@ -18,8 +18,8 @@ class CardsController < ApplicationController end format.json do - card = @board.cards.create! card_params.merge(creator: Current.user, status: "published") - head :created, location: card_path(card, format: :json) + @card = @board.cards.create! card_params.merge(creator: Current.user, status: "published") + render :show, status: :created, location: card_path(@card, format: :json) end end end diff --git a/app/controllers/columns/left_positions_controller.rb b/app/controllers/columns/left_positions_controller.rb index 7161c093f..78e0c0c80 100644 --- a/app/controllers/columns/left_positions_controller.rb +++ b/app/controllers/columns/left_positions_controller.rb @@ -4,5 +4,10 @@ class Columns::LeftPositionsController < ApplicationController def create @left_column = @column.left_column @column.move_left + + respond_to do |format| + format.turbo_stream + format.json { head :created } + end end end diff --git a/app/controllers/columns/right_positions_controller.rb b/app/controllers/columns/right_positions_controller.rb index d43beb662..5d9cc37f9 100644 --- a/app/controllers/columns/right_positions_controller.rb +++ b/app/controllers/columns/right_positions_controller.rb @@ -4,5 +4,10 @@ class Columns::RightPositionsController < ApplicationController def create @right_column = @column.right_column @column.move_right + + respond_to do |format| + format.turbo_stream + format.json { head :created } + end end end diff --git a/app/controllers/my/access_tokens_controller.rb b/app/controllers/my/access_tokens_controller.rb index 82cd93842..c9fcf38c3 100644 --- a/app/controllers/my/access_tokens_controller.rb +++ b/app/controllers/my/access_tokens_controller.rb @@ -1,4 +1,6 @@ class My::AccessTokensController < ApplicationController + skip_before_action :require_account + def index @access_tokens = my_access_tokens.order(created_at: :desc) end @@ -24,14 +26,19 @@ class My::AccessTokensController < ApplicationController format.json do render status: :created, json: \ - { token: access_token.token, description: access_token.description, permission: access_token.permission } + { id: access_token.id, token: access_token.token, description: access_token.description, + permission: access_token.permission, created_at: access_token.created_at.utc } end end end def destroy my_access_tokens.find(params[:id]).destroy! - redirect_to my_access_tokens_path + + respond_to do |format| + format.html { redirect_to my_access_tokens_path } + format.json { head :no_content } + end end private diff --git a/app/controllers/notifications/settings_controller.rb b/app/controllers/notifications/settings_controller.rb index c4aab7f69..252abec2e 100644 --- a/app/controllers/notifications/settings_controller.rb +++ b/app/controllers/notifications/settings_controller.rb @@ -1,4 +1,6 @@ class Notifications::SettingsController < ApplicationController + wrap_parameters :user_settings + before_action :set_settings def show @@ -7,7 +9,11 @@ class Notifications::SettingsController < ApplicationController def update @settings.update!(settings_params) - redirect_to notifications_settings_path, notice: "Settings updated" + + respond_to do |format| + format.html { redirect_to notifications_settings_path, notice: "Settings updated" } + format.json { head :no_content } + end end private diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index b1ec5a0d2..70dfa4d4d 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -5,10 +5,22 @@ class SearchesController < ApplicationController @query = params[:q].blank? ? nil : params[:q] if card = Current.user.accessible_cards.find_by_id(@query) - @card = card + respond_to do |format| + format.html { @card = card } + format.json { set_page_and_extract_portion_from Current.user.accessible_cards.where(id: card.id) } + end else - set_page_and_extract_portion_from Current.user.search(@query) - @recent_search_queries = Current.user.search_queries.order(updated_at: :desc).limit(10) + respond_to do |format| + format.html do + set_page_and_extract_portion_from Current.user.search(@query) + @recent_search_queries = Current.user.search_queries.order(updated_at: :desc).limit(10) + end + + format.json do + set_page_and_extract_portion_from \ + Current.user.accessible_cards.mentioning(@query, user: Current.user).distinct.latest.preloaded + end + end end end end diff --git a/app/controllers/users/avatars_controller.rb b/app/controllers/users/avatars_controller.rb index 55d8f06dd..544398331 100644 --- a/app/controllers/users/avatars_controller.rb +++ b/app/controllers/users/avatars_controller.rb @@ -16,7 +16,11 @@ class Users::AvatarsController < ApplicationController def destroy @user.avatar.destroy - redirect_to @user + + respond_to do |format| + format.html { redirect_to @user } + format.json { head :no_content } + end end private diff --git a/app/controllers/users/push_subscriptions_controller.rb b/app/controllers/users/push_subscriptions_controller.rb index b511a6b19..91b47264c 100644 --- a/app/controllers/users/push_subscriptions_controller.rb +++ b/app/controllers/users/push_subscriptions_controller.rb @@ -5,12 +5,21 @@ class Users::PushSubscriptionsController < ApplicationController end def create - @push_subscriptions.create_with(user_agent: request.user_agent).create_or_find_by!(push_subscription_params) + subscription = @push_subscriptions.create_with(user_agent: request.user_agent).create_or_find_by!(push_subscription_params) + + respond_to do |format| + format.html { head :no_content } + format.json { head :created } + end end def destroy @push_subscriptions.destroy_by(id: params[:id]) - redirect_to user_push_subscriptions_url + + respond_to do |format| + format.html { redirect_to user_push_subscriptions_url } + format.json { head :no_content } + end end private diff --git a/app/controllers/users/roles_controller.rb b/app/controllers/users/roles_controller.rb index 711308d13..fb11d4e08 100644 --- a/app/controllers/users/roles_controller.rb +++ b/app/controllers/users/roles_controller.rb @@ -1,10 +1,16 @@ class Users::RolesController < ApplicationController + wrap_parameters :user + before_action :set_user before_action :ensure_permission_to_administer_user def update @user.update!(role_params) - redirect_to account_settings_path + + respond_to do |format| + format.html { redirect_to account_settings_path } + format.json { head :no_content } + end end private diff --git a/app/views/account/exports/show.json.jbuilder b/app/views/account/exports/show.json.jbuilder new file mode 100644 index 000000000..2bd9e3d60 --- /dev/null +++ b/app/views/account/exports/show.json.jbuilder @@ -0,0 +1,6 @@ +json.(@export, :id, :status) +json.created_at @export.created_at.utc + +if @export.completed? && @export.file.attached? + json.download_url rails_blob_url(@export.file, disposition: "attachment") +end diff --git a/app/views/account/join_codes/show.json.jbuilder b/app/views/account/join_codes/show.json.jbuilder new file mode 100644 index 000000000..559e7b6c2 --- /dev/null +++ b/app/views/account/join_codes/show.json.jbuilder @@ -0,0 +1,3 @@ +json.(@join_code, :code, :usage_count, :usage_limit) +json.url join_url(code: @join_code.code, script_name: Current.account.slug) +json.active !!@join_code.active? diff --git a/app/views/account/settings/show.json.jbuilder b/app/views/account/settings/show.json.jbuilder new file mode 100644 index 000000000..9a62dabc6 --- /dev/null +++ b/app/views/account/settings/show.json.jbuilder @@ -0,0 +1 @@ +json.(Current.account, :name) diff --git a/app/views/boards/columns/closeds/show.json.jbuilder b/app/views/boards/columns/closeds/show.json.jbuilder new file mode 100644 index 000000000..c1dc1dff1 --- /dev/null +++ b/app/views/boards/columns/closeds/show.json.jbuilder @@ -0,0 +1 @@ +json.array! @page.records, partial: "cards/card", as: :card diff --git a/app/views/boards/columns/not_nows/show.json.jbuilder b/app/views/boards/columns/not_nows/show.json.jbuilder new file mode 100644 index 000000000..c1dc1dff1 --- /dev/null +++ b/app/views/boards/columns/not_nows/show.json.jbuilder @@ -0,0 +1 @@ +json.array! @page.records, partial: "cards/card", as: :card diff --git a/app/views/boards/columns/streams/show.json.jbuilder b/app/views/boards/columns/streams/show.json.jbuilder new file mode 100644 index 000000000..c1dc1dff1 --- /dev/null +++ b/app/views/boards/columns/streams/show.json.jbuilder @@ -0,0 +1 @@ +json.array! @page.records, partial: "cards/card", as: :card diff --git a/app/views/cards/steps/index.json.jbuilder b/app/views/cards/steps/index.json.jbuilder new file mode 100644 index 000000000..087790175 --- /dev/null +++ b/app/views/cards/steps/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @card.steps, partial: "cards/steps/step", as: :step diff --git a/app/views/my/access_tokens/_access_token.json.jbuilder b/app/views/my/access_tokens/_access_token.json.jbuilder new file mode 100644 index 000000000..3e0a08084 --- /dev/null +++ b/app/views/my/access_tokens/_access_token.json.jbuilder @@ -0,0 +1,2 @@ +json.(access_token, :id, :description, :permission) +json.created_at access_token.created_at.utc diff --git a/app/views/my/access_tokens/index.json.jbuilder b/app/views/my/access_tokens/index.json.jbuilder new file mode 100644 index 000000000..4abdba5f7 --- /dev/null +++ b/app/views/my/access_tokens/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @access_tokens, partial: "my/access_tokens/access_token", as: :access_token diff --git a/app/views/notifications/settings/show.json.jbuilder b/app/views/notifications/settings/show.json.jbuilder new file mode 100644 index 000000000..6eaea0893 --- /dev/null +++ b/app/views/notifications/settings/show.json.jbuilder @@ -0,0 +1 @@ +json.bundle_email_frequency Current.user.settings.bundle_email_frequency diff --git a/app/views/reactions/show.json.jbuilder b/app/views/reactions/show.json.jbuilder new file mode 100644 index 000000000..a61ed5902 --- /dev/null +++ b/app/views/reactions/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "reactions/reaction", reaction: @reaction diff --git a/app/views/searches/show.json.jbuilder b/app/views/searches/show.json.jbuilder new file mode 100644 index 000000000..c1dc1dff1 --- /dev/null +++ b/app/views/searches/show.json.jbuilder @@ -0,0 +1 @@ +json.array! @page.records, partial: "cards/card", as: :card diff --git a/test/controllers/accounts/entropies_controller_test.rb b/test/controllers/accounts/entropies_controller_test.rb index ef23851de..5903ed7d0 100644 --- a/test/controllers/accounts/entropies_controller_test.rb +++ b/test/controllers/accounts/entropies_controller_test.rb @@ -13,6 +13,13 @@ class Account::EntropiesControllerTest < ActionDispatch::IntegrationTest assert_redirected_to account_settings_path end + test "update as JSON" do + put account_entropy_path, params: { entropy: { auto_postpone_period: 2.days } }, as: :json + + assert_response :no_content + assert_equal 2.days, entropies("37s_account").reload.auto_postpone_period + end + test "update requires admin" do logout_and_sign_in_as :david diff --git a/test/controllers/accounts/exports_controller_test.rb b/test/controllers/accounts/exports_controller_test.rb index ebcf26117..fd950f989 100644 --- a/test/controllers/accounts/exports_controller_test.rb +++ b/test/controllers/accounts/exports_controller_test.rb @@ -76,6 +76,49 @@ class Account::ExportsControllerTest < ActionDispatch::IntegrationTest assert_select "h2", "Download Expired" end + test "create as JSON" do + assert_difference -> { Account::Export.count }, 1 do + assert_enqueued_with(job: DataExportJob) do + post account_exports_path, as: :json + end + end + + assert_response :created + body = @response.parsed_body + assert body["id"].present? + assert_equal "pending", body["status"] + assert_nil body["download_url"] + end + + test "show as JSON with completed export" do + export = Account::Export.create!(account: Current.account, user: users(:jason)) + export.build + + get account_export_path(export), as: :json + assert_response :success + + body = @response.parsed_body + assert_equal export.id, body["id"] + assert_equal "completed", body["status"] + assert body["download_url"].present? + end + + test "show as JSON with pending export" do + export = Account::Export.create!(account: Current.account, user: users(:jason)) + + get account_export_path(export), as: :json + assert_response :success + + body = @response.parsed_body + assert_equal "pending", body["status"] + assert_nil body["download_url"] + end + + test "show as JSON with missing export" do + get account_export_path("nonexistent"), as: :json + assert_response :not_found + end + test "create is forbidden for non-admin members" do logout_and_sign_in_as :david diff --git a/test/controllers/accounts/join_codes_controller_test.rb b/test/controllers/accounts/join_codes_controller_test.rb index 15d41b5ec..a56472c5c 100644 --- a/test/controllers/accounts/join_codes_controller_test.rb +++ b/test/controllers/accounts/join_codes_controller_test.rb @@ -24,6 +24,41 @@ class Account::JoinCodesControllerTest < ActionDispatch::IntegrationTest assert_redirected_to account_join_code_path end + test "show as JSON" do + get account_join_code_path, as: :json + assert_response :success + + body = @response.parsed_body + assert body["code"].present? + assert body.key?("usage_count") + assert body.key?("usage_limit") + assert body.key?("url") + assert body.key?("active") + end + + test "update as JSON" do + put account_join_code_path, params: { account_join_code: { usage_limit: 5 } }, as: :json + + assert_response :no_content + assert_equal 5, Current.account.join_code.reload.usage_limit + end + + test "update as JSON with invalid data" do + huge_number = "99999999999999999999999999999999999" + + put account_join_code_path, params: { account_join_code: { usage_limit: huge_number } }, as: :json + + assert_response :unprocessable_entity + end + + test "destroy as JSON" do + assert_changes -> { Current.account.join_code.reload.code } do + delete account_join_code_path, as: :json + end + + assert_response :no_content + end + test "update requires admin" do logout_and_sign_in_as :david diff --git a/test/controllers/accounts/settings_controller_test.rb b/test/controllers/accounts/settings_controller_test.rb index 9f17a74a7..6f3fba5b0 100644 --- a/test/controllers/accounts/settings_controller_test.rb +++ b/test/controllers/accounts/settings_controller_test.rb @@ -16,6 +16,20 @@ class Account::SettingsControllerTest < ActionDispatch::IntegrationTest assert_redirected_to account_settings_path end + test "show as JSON" do + get account_settings_path, as: :json + assert_response :success + + assert_equal Current.account.name, @response.parsed_body["name"] + end + + test "update as JSON" do + put account_settings_path, params: { account: { name: "New Account Name" } }, as: :json + + assert_response :no_content + assert_equal "New Account Name", Current.account.reload.name + end + test "update requires admin" do logout_and_sign_in_as :david diff --git a/test/controllers/api/flat_json_params_test.rb b/test/controllers/api/flat_json_params_test.rb new file mode 100644 index 000000000..4efba5209 --- /dev/null +++ b/test/controllers/api/flat_json_params_test.rb @@ -0,0 +1,71 @@ +require "test_helper" + +class FlatJsonParamsTest < ActionDispatch::IntegrationTest + setup do + sign_in_as :kevin + end + + test "update user role with flat JSON" do + put user_role_path(users(:david)), params: { role: "admin" }, as: :json + + assert_response :no_content + assert users(:david).reload.admin? + end + + test "update notification settings with flat JSON" do + logout_and_sign_in_as :david + + assert_changes -> { users(:david).reload.settings.bundle_email_frequency }, from: "never", to: "every_few_hours" do + put notifications_settings_path, params: { bundle_email_frequency: "every_few_hours" }, as: :json + end + + assert_response :no_content + end + + test "update join code with flat JSON" do + put account_join_code_path, params: { usage_limit: 5 }, as: :json + + assert_response :no_content + assert_equal 5, Current.account.join_code.reload.usage_limit + end + + test "update account settings with flat JSON" do + put account_settings_path, params: { name: "New Name" }, as: :json + + assert_response :no_content + assert_equal "New Name", Current.account.reload.name + end + + test "update board entropy with flat JSON" do + board = boards(:writebook) + + put board_entropy_path(board), params: { auto_postpone_period: 99.days }, as: :json + + assert_response :no_content + assert_equal 99.days, board.entropy.reload.auto_postpone_period + end + + test "update account entropy with flat JSON" do + put account_entropy_path, params: { auto_postpone_period: 2.days }, as: :json + + assert_response :no_content + assert_equal 2.days, Current.account.entropy.reload.auto_postpone_period + end + + test "create push subscription with flat JSON" do + stub_dns_resolution("142.250.185.206") + + post user_push_subscriptions_path(users(:kevin)), + params: { endpoint: "https://fcm.googleapis.com/fcm/send/abc123", p256dh_key: "key1", auth_key: "key2" }, + as: :json + + assert_response :created + end + + private + def stub_dns_resolution(*ips) + dns_mock = mock("dns") + dns_mock.stubs(:each_address).multiple_yields(*ips) + Resolv::DNS.stubs(:open).yields(dns_mock) + end +end diff --git a/test/controllers/boards/columns/closeds_controller_test.rb b/test/controllers/boards/columns/closeds_controller_test.rb index f147a2106..90b6c8756 100644 --- a/test/controllers/boards/columns/closeds_controller_test.rb +++ b/test/controllers/boards/columns/closeds_controller_test.rb @@ -9,4 +9,11 @@ class Boards::Columns::ClosedsControllerTest < ActionDispatch::IntegrationTest get board_columns_closed_path(boards(:writebook)) assert_response :success end + + test "show as JSON" do + get board_columns_closed_path(boards(:writebook)), as: :json + assert_response :success + + assert_kind_of Array, @response.parsed_body + end end diff --git a/test/controllers/boards/columns/not_nows_controller_test.rb b/test/controllers/boards/columns/not_nows_controller_test.rb index 29088728c..6f9efbf3e 100644 --- a/test/controllers/boards/columns/not_nows_controller_test.rb +++ b/test/controllers/boards/columns/not_nows_controller_test.rb @@ -9,4 +9,11 @@ class Boards::Columns::NotNowsControllerTest < ActionDispatch::IntegrationTest get board_columns_not_now_path(boards(:writebook)) assert_response :success end + + test "show as JSON" do + get board_columns_not_now_path(boards(:writebook)), as: :json + assert_response :success + + assert_kind_of Array, @response.parsed_body + end end diff --git a/test/controllers/boards/columns/streams_controller_test.rb b/test/controllers/boards/columns/streams_controller_test.rb index fec9603cc..0fbc13afd 100644 --- a/test/controllers/boards/columns/streams_controller_test.rb +++ b/test/controllers/boards/columns/streams_controller_test.rb @@ -9,4 +9,12 @@ class Boards::Columns::StreamsControllerTest < ActionDispatch::IntegrationTest get board_columns_stream_path(boards(:writebook)) assert_response :success end + + test "show as JSON" do + get board_columns_stream_path(boards(:writebook)), as: :json + assert_response :success + + assert_kind_of Array, @response.parsed_body + assert response.headers["X-Total-Count"].present?, "Expected X-Total-Count header" + end end diff --git a/test/controllers/boards/columns_controller_test.rb b/test/controllers/boards/columns_controller_test.rb index b9c974321..9fef675f5 100644 --- a/test/controllers/boards/columns_controller_test.rb +++ b/test/controllers/boards/columns_controller_test.rb @@ -75,6 +75,7 @@ class Boards::ColumnsControllerTest < ActionDispatch::IntegrationTest assert_response :created assert_equal board_column_path(board, Column.last, format: :json), @response.headers["Location"] + assert_equal "New Column", @response.parsed_body["name"] end test "update as JSON" do diff --git a/test/controllers/boards/entropies_controller_test.rb b/test/controllers/boards/entropies_controller_test.rb index 0c989c3cd..c0ec089a5 100644 --- a/test/controllers/boards/entropies_controller_test.rb +++ b/test/controllers/boards/entropies_controller_test.rb @@ -16,6 +16,13 @@ class Boards::EntropiesControllerTest < ActionDispatch::IntegrationTest end end + test "update as JSON" do + put board_entropy_path(@board), params: { board: { auto_postpone_period: 99.days } }, as: :json + + assert_response :no_content + assert_equal 99.days, @board.entropy.reload.auto_postpone_period + end + test "update requires board admin permission" do logout_and_sign_in_as :jz diff --git a/test/controllers/boards/involvements_controller_test.rb b/test/controllers/boards/involvements_controller_test.rb index 45587176a..4045df743 100644 --- a/test/controllers/boards/involvements_controller_test.rb +++ b/test/controllers/boards/involvements_controller_test.rb @@ -15,4 +15,15 @@ class Boards::InvolvementsControllerTest < ActionDispatch::IntegrationTest assert_response :success end + + test "update as JSON" do + board = boards(:writebook) + board.access_for(users(:kevin)).access_only! + + assert_changes -> { board.access_for(users(:kevin)).involvement }, from: "access_only", to: "watching" do + put board_involvement_path(board), params: { involvement: "watching" }, as: :json + end + + assert_response :no_content + end end diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb index 7dd2b2f06..fedd9d188 100644 --- a/test/controllers/boards_controller_test.rb +++ b/test/controllers/boards_controller_test.rb @@ -275,6 +275,7 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest assert_response :created assert_equal board_path(Board.last, format: :json), @response.headers["Location"] + assert_equal "My new board", @response.parsed_body["name"] end test "update as JSON" do diff --git a/test/controllers/cards/comments/reactions_controller_test.rb b/test/controllers/cards/comments/reactions_controller_test.rb index 21a94c5f3..eda47536f 100644 --- a/test/controllers/cards/comments/reactions_controller_test.rb +++ b/test/controllers/cards/comments/reactions_controller_test.rb @@ -49,6 +49,7 @@ class Cards::Comments::ReactionsControllerTest < ActionDispatch::IntegrationTest end assert_response :created + assert_equal "👍", @response.parsed_body["content"] end test "destroy as JSON" do diff --git a/test/controllers/cards/comments_controller_test.rb b/test/controllers/cards/comments_controller_test.rb index a6e83cbef..eebd62a9c 100644 --- a/test/controllers/cards/comments_controller_test.rb +++ b/test/controllers/cards/comments_controller_test.rb @@ -56,6 +56,7 @@ class Cards::CommentsControllerTest < ActionDispatch::IntegrationTest assert_response :created assert_equal card_comment_path(card, Comment.last, format: :json), @response.headers["Location"] + assert_equal Comment.last.id, @response.parsed_body["id"] end test "create as JSON with custom created_at" do diff --git a/test/controllers/cards/publishes_controller_test.rb b/test/controllers/cards/publishes_controller_test.rb index 537a6f73b..1a6325e2f 100644 --- a/test/controllers/cards/publishes_controller_test.rb +++ b/test/controllers/cards/publishes_controller_test.rb @@ -16,6 +16,17 @@ class Cards::PublishesControllerTest < ActionDispatch::IntegrationTest assert_redirected_to card.board end + test "create as JSON" do + card = cards(:logo) + card.drafted! + + assert_changes -> { card.reload.published? }, from: false, to: true do + post card_publish_path(card), as: :json + end + + assert_response :created + end + test "create and add another" do card = cards(:logo) card.drafted! diff --git a/test/controllers/cards/reactions_controller_test.rb b/test/controllers/cards/reactions_controller_test.rb index 86ab22639..731d606d8 100644 --- a/test/controllers/cards/reactions_controller_test.rb +++ b/test/controllers/cards/reactions_controller_test.rb @@ -53,6 +53,7 @@ class Cards::ReactionsControllerTest < ActionDispatch::IntegrationTest end assert_response :created + assert_equal "👍", @response.parsed_body["content"] end test "destroy as JSON" do diff --git a/test/controllers/cards/readings_controller_test.rb b/test/controllers/cards/readings_controller_test.rb index 0c9c9aeed..4aaec41d7 100644 --- a/test/controllers/cards/readings_controller_test.rb +++ b/test/controllers/cards/readings_controller_test.rb @@ -39,6 +39,18 @@ class Cards::ReadingsControllerTest < ActionDispatch::IntegrationTest assert_response :success end + test "create as JSON" do + post card_reading_url(cards(:logo)), as: :json + assert_response :created + end + + test "destroy as JSON" do + notifications(:logo_assignment_kevin).read + + delete card_reading_url(cards(:logo)), as: :json + assert_response :no_content + end + test "unread notification on destroy" do notifications(:logo_assignment_kevin).read diff --git a/test/controllers/cards/steps_controller_test.rb b/test/controllers/cards/steps_controller_test.rb index 039fbf4c4..011b68812 100644 --- a/test/controllers/cards/steps_controller_test.rb +++ b/test/controllers/cards/steps_controller_test.rb @@ -53,6 +53,19 @@ class Cards::StepsControllerTest < ActionDispatch::IntegrationTest end end + test "index as JSON" do + card = cards(:logo) + card.steps.create!(content: "Step one") + card.steps.create!(content: "Step two", completed: true) + + get card_steps_path(card), as: :json + assert_response :success + + body = @response.parsed_body + assert_equal 2, body.size + assert_equal "Step one", body.first["content"] + end + test "create as JSON" do card = cards(:logo) @@ -62,6 +75,7 @@ class Cards::StepsControllerTest < ActionDispatch::IntegrationTest assert_response :created assert_equal card_step_path(card, Step.last, format: :json), @response.headers["Location"] + assert_equal "New step", @response.parsed_body["content"] end test "show as JSON" do diff --git a/test/controllers/cards_controller_test.rb b/test/controllers/cards_controller_test.rb index 42e24d927..901a584f9 100644 --- a/test/controllers/cards_controller_test.rb +++ b/test/controllers/cards_controller_test.rb @@ -192,6 +192,7 @@ class CardsControllerTest < ActionDispatch::IntegrationTest card = Card.last assert_equal card_path(card, format: :json), @response.headers["Location"] + assert_equal "My new card", @response.parsed_body["title"] assert_equal "My new card", card.title assert_equal "Big if true", card.description.to_plain_text diff --git a/test/controllers/columns/left_positions_controller_test.rb b/test/controllers/columns/left_positions_controller_test.rb index 6c807b0e5..5f1c0cd26 100644 --- a/test/controllers/columns/left_positions_controller_test.rb +++ b/test/controllers/columns/left_positions_controller_test.rb @@ -21,6 +21,22 @@ class Columns::LeftPositionsControllerTest < ActionDispatch::IntegrationTest assert_equal original_position_a, column_b.reload.position end + test "move column left as JSON" do + board = boards(:writebook) + columns = board.columns.sorted.to_a + + column_a = columns[0] + column_b = columns[1] + original_position_a = column_a.position + original_position_b = column_b.position + + post column_left_position_path(column_b), as: :json + assert_response :created + + assert_equal original_position_b, column_a.reload.position + assert_equal original_position_a, column_b.reload.position + end + test "move left refreshes adjacent columns" do column = columns(:writebook_in_progress) diff --git a/test/controllers/columns/right_positions_controller_test.rb b/test/controllers/columns/right_positions_controller_test.rb index c927531be..1ee7a0987 100644 --- a/test/controllers/columns/right_positions_controller_test.rb +++ b/test/controllers/columns/right_positions_controller_test.rb @@ -21,6 +21,22 @@ class Columns::RightPositionsControllerTest < ActionDispatch::IntegrationTest assert_equal original_position_a, column_b.reload.position end + test "move column right as JSON" do + board = boards(:writebook) + columns = board.columns.sorted.to_a + + column_a = columns[0] + column_b = columns[1] + original_position_a = column_a.position + original_position_b = column_b.position + + post column_right_position_path(column_a), as: :json + assert_response :created + + assert_equal original_position_b, column_a.reload.position + assert_equal original_position_a, column_b.reload.position + end + test "move right refreshes adjacent columns" do column = columns(:writebook_in_progress) diff --git a/test/controllers/my/access_tokens_controller_test.rb b/test/controllers/my/access_tokens_controller_test.rb index 176302336..bced907db 100644 --- a/test/controllers/my/access_tokens_controller_test.rb +++ b/test/controllers/my/access_tokens_controller_test.rb @@ -25,9 +25,11 @@ class My::AccessTokensControllerTest < ActionDispatch::IntegrationTest end assert_response :created body = @response.parsed_body + assert body["id"].present? assert body["token"].present? assert_equal "Fizzy CLI", body["description"] assert_equal "write", body["permission"] + assert body["created_at"].present? end test "create new token via JSON with bearer token" do @@ -54,6 +56,36 @@ class My::AccessTokensControllerTest < ActionDispatch::IntegrationTest assert_response :unauthorized end + test "index as JSON" do + get my_access_tokens_path, as: :json + assert_response :success + + body = @response.parsed_body + assert_kind_of Array, body + end + + test "index as JSON with bearer token and no account scope" do + sign_out + bearer_token = { "HTTP_AUTHORIZATION" => "Bearer #{identity_access_tokens(:davids_api_token).token}" } + + untenanted do + get my_access_tokens_path, as: :json, env: bearer_token + end + + assert_response :success + assert_kind_of Array, @response.parsed_body + end + + test "destroy as JSON" do + token = identities(:kevin).access_tokens.create!(description: "To delete", permission: "read") + + assert_difference -> { identities(:kevin).access_tokens.count }, -1 do + delete my_access_token_path(token), as: :json + end + + assert_response :no_content + end + test "accessing new token after reveal window redirects to index" do assert_changes -> { identities(:kevin).access_tokens.count }, +1 do post my_access_tokens_path, params: { access_token: { description: "GitHub", permission: "read" } } diff --git a/test/controllers/notifications/settings_controller_test.rb b/test/controllers/notifications/settings_controller_test.rb index d8472bb02..b5b554c34 100644 --- a/test/controllers/notifications/settings_controller_test.rb +++ b/test/controllers/notifications/settings_controller_test.rb @@ -13,6 +13,21 @@ class Notifications::SettingsControllerTest < ActionDispatch::IntegrationTest assert_response :success end + test "show as JSON" do + get notifications_settings_path, as: :json + assert_response :success + + assert_equal @user.settings.bundle_email_frequency, @response.parsed_body["bundle_email_frequency"] + end + + test "update as JSON" do + assert_changes -> { @user.reload.settings.bundle_email_frequency }, from: "never", to: "every_few_hours" do + put notifications_settings_path, params: { user_settings: { bundle_email_frequency: "every_few_hours" } }, as: :json + end + + assert_response :no_content + end + test "update email frequency" do assert_changes -> { @user.reload.settings.bundle_email_frequency }, from: "never", to: "every_few_hours" do put notifications_settings_path, params: { user_settings: { bundle_email_frequency: "every_few_hours" } } diff --git a/test/controllers/searches_controller_test.rb b/test/controllers/searches_controller_test.rb index 11bac3bc9..b0172cdf5 100644 --- a/test/controllers/searches_controller_test.rb +++ b/test/controllers/searches_controller_test.rb @@ -46,6 +46,36 @@ class SearchesControllerTest < ActionDispatch::IntegrationTest assert_select ".search__blank-slate", text: "No matches" end + test "search as JSON" do + get search_path(q: "broken", script_name: "/#{@account.external_account_id}"), as: :json + assert_response :success + + body = @response.parsed_body + assert_kind_of Array, body + assert_equal 1, body.size + assert_equal "Layout is broken", body.first["title"] + end + + test "search by card ID as JSON returns array" do + get search_path(q: @card.id, script_name: "/#{@account.external_account_id}"), as: :json + assert_response :success + + body = @response.parsed_body + assert_kind_of Array, body + assert_equal 1, body.size + assert_equal @card.id, body.first["id"] + end + + test "search as JSON deduplicates cards with multiple search hits" do + get search_path(q: "haggis", script_name: "/#{@account.external_account_id}"), as: :json + assert_response :success + + body = @response.parsed_body + assert_kind_of Array, body + assert_equal 1, body.size + assert_equal @comment2_card.id, body.first["id"] + end + test "search highlights matched terms with proper HTML marks" do @board.cards.create!(title: "Testing search highlighting", status: "published", creator: @user) diff --git a/test/controllers/users/avatars_controller_test.rb b/test/controllers/users/avatars_controller_test.rb index 3ded884b8..ac213e46b 100644 --- a/test/controllers/users/avatars_controller_test.rb +++ b/test/controllers/users/avatars_controller_test.rb @@ -49,6 +49,11 @@ class Users::AvatarsControllerTest < ActionDispatch::IntegrationTest assert_redirected_to users(:david) end + test "delete self as JSON" do + delete user_avatar_path(users(:david)), as: :json + assert_response :no_content + end + test "unable to delete other" do delete user_avatar_path(users(:kevin)) assert_response :forbidden diff --git a/test/controllers/users/push_subscriptions_controller_test.rb b/test/controllers/users/push_subscriptions_controller_test.rb index 4b4dd0a09..b9f43ff0a 100644 --- a/test/controllers/users/push_subscriptions_controller_test.rb +++ b/test/controllers/users/push_subscriptions_controller_test.rb @@ -20,6 +20,42 @@ class Users::PushSubscriptionsControllerTest < ActionDispatch::IntegrationTest assert_equal "Mozilla/5.0", users(:david).push_subscriptions.last.user_agent end + test "create as JSON" do + subscription_params = { "endpoint" => "https://fcm.googleapis.com/fcm/send/abc123", "p256dh_key" => "123", "auth_key" => "456" } + + post user_push_subscriptions_path(users(:david)), + params: { push_subscription: subscription_params }, headers: { "HTTP_USER_AGENT" => "Mozilla/5.0" }, as: :json + + assert_response :created + end + + test "create as JSON for duplicate subscription" do + subscription_params = { "endpoint" => "https://fcm.googleapis.com/fcm/send/abc123", "p256dh_key" => "123", "auth_key" => "456" } + + users(:david).push_subscriptions.create!(subscription_params) + + assert_no_difference -> { Push::Subscription.count } do + post user_push_subscriptions_path(users(:david)), + params: { push_subscription: subscription_params }, as: :json + end + + assert_response :created + end + + test "destroy as JSON" do + subscription = users(:david).push_subscriptions.create!( + endpoint: "https://fcm.googleapis.com/fcm/send/abc123", + p256dh_key: "123", + auth_key: "456" + ) + + assert_difference -> { Push::Subscription.count }, -1 do + delete user_push_subscription_path(users(:david), subscription), as: :json + end + + assert_response :no_content + end + test "destroy a push subscription" do subscription = users(:david).push_subscriptions.create!( endpoint: "https://fcm.googleapis.com/fcm/send/abc123", diff --git a/test/controllers/users/roles_controller_test.rb b/test/controllers/users/roles_controller_test.rb index 37d1f2067..d0091bc53 100644 --- a/test/controllers/users/roles_controller_test.rb +++ b/test/controllers/users/roles_controller_test.rb @@ -14,6 +14,13 @@ class Users::RolesControllerTest < ActionDispatch::IntegrationTest assert users(:david).reload.admin? end + test "update as JSON" do + put user_role_path(users(:david)), params: { user: { role: "admin" } }, as: :json + + assert_response :no_content + assert users(:david).reload.admin? + end + test "can't promote to special roles" do assert_no_changes -> { users(:david).reload.role } do put user_role_path(users(:david)), params: { user: { role: "system" } }