From bfffa0f8a623405575e6c33fdba8500f17f0b68e Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:26:07 +0300 Subject: [PATCH 01/10] minor YARD fix for the default_filter users helper --- app/helpers/users_helper.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index b71561afd..f5b90f1b8 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -47,10 +47,9 @@ def preference_choice(pref_config) end end - ## - # Get the default filter for the specified user and category. - # @param user_id [Integer] - # @param category_id [Category] + # Get the default filter for a given user and category +id+. + # @param user_id [Integer] +id+ of the user to get default filter for + # @param category_id [Integer] +id+ of the category to get default filter for # @return [Filter, nil] def default_filter(user_id, category_id) CategoryFilterDefault.find_by(user_id: user_id, category_id: category_id)&.filter From ef524718c2afbe8d712b66840a9fffbf63e5bd99 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:27:18 +0300 Subject: [PATCH 02/10] switched default_filter action's response status to a constant --- app/controllers/users_controller.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index fa7cc317d..be7577f67 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -179,11 +179,10 @@ def delete_filter def default_filter if user_signed_in? && params[:category] default_filter = helpers.default_filter(current_user.id, params[:category].to_i) - render json: { status: 'success', success: true, name: default_filter&.name }, - status: 200 + render json: { status: 'success', success: true, name: default_filter&.name } else render json: { status: 'failed', success: false }, - status: 400 + status: :bad_request end end From d00f8ad10cefd64e95f90272f3010bf4d2d2b80f Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:29:08 +0300 Subject: [PATCH 03/10] minor improvement to default_filter action's check for category param --- app/controllers/users_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index be7577f67..adf289d1e 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -177,7 +177,7 @@ def delete_filter end def default_filter - if user_signed_in? && params[:category] + if user_signed_in? && params[:category].present? default_filter = helpers.default_filter(current_user.id, params[:category].to_i) render json: { status: 'success', success: true, name: default_filter&.name } else From c2e717946c8b0cc7b41a987f150abfe0e4b924c5 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:29:31 +0300 Subject: [PATCH 04/10] reduced reliance of filters JS asset on jQuery --- app/assets/javascripts/filters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js index d929ffb26..1ecf5ab23 100644 --- a/app/assets/javascripts/filters.js +++ b/app/assets/javascripts/filters.js @@ -1,4 +1,4 @@ -$(() => { +document.addEventListener('DOMContentLoaded', () => { $('.js-filter-select').toArray().forEach(async (el) => { const $select = $(el); const $form = $select.closest('form'); From 09e75ee7c8403a25227a970f46be1d7e6f1342d1 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:30:46 +0300 Subject: [PATCH 05/10] properly linted filters JS asset --- app/assets/javascripts/filters.js | 274 +++++++++++++++--------------- 1 file changed, 141 insertions(+), 133 deletions(-) diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js index 1ecf5ab23..274c41dbf 100644 --- a/app/assets/javascripts/filters.js +++ b/app/assets/javascripts/filters.js @@ -1,158 +1,166 @@ document.addEventListener('DOMContentLoaded', () => { - $('.js-filter-select').toArray().forEach(async (el) => { - const $select = $(el); - const $form = $select.closest('form'); - const $formFilters = $form.find('.form--filter'); - const $saveButton = $form.find('.filter-save'); - const $isDefaultCheckbox = $form.find('.filter-is-default'); - const categoryId = $isDefaultCheckbox.val()?.toString(); - let defaultFilter = await QPixel.defaultFilter(categoryId); - const $deleteButton = $form.find('.filter-delete'); - - // Enables/Disables Save & Delete buttons programatically - async function computeEnables() { - const filters = await QPixel.filters(); - const filterName = $select.val()?.toString(); - - // Nothing set - if (!filterName) { - $saveButton.prop('disabled', true); - $deleteButton.prop('disabled', true); - return; - } - - const filter = filters[filterName] - - // New filter - if (!filter) { - $saveButton.prop('disabled', false); - $deleteButton.prop('disabled', true); - return; - } + $('.js-filter-select') + .toArray() + .forEach(async (el) => { + const $select = $(el); + const $form = $select.closest('form'); + const $formFilters = $form.find('.form--filter'); + const $saveButton = $form.find('.filter-save'); + const $isDefaultCheckbox = $form.find('.filter-is-default'); + const categoryId = $isDefaultCheckbox.val()?.toString(); + let defaultFilter = await QPixel.defaultFilter(categoryId); + const $deleteButton = $form.find('.filter-delete'); + + // Enables/Disables Save & Delete buttons programatically + async function computeEnables() { + const filters = await QPixel.filters(); + const filterName = $select.val()?.toString(); + + // Nothing set + if (!filterName) { + $saveButton.prop('disabled', true); + $deleteButton.prop('disabled', true); + return; + } - // Not a new filter - $deleteButton.prop('disabled', filter.system); + const filter = filters[filterName]; - const hasChanges = [...$formFilters].some((el) => { - const filterValue = filter[el.dataset.name]; - let elValue = /** @type {string | undefined[]} */ ($(el).val()); - if (filterValue?.constructor == Array) { - elValue = elValue ?? []; - return filterValue.length != elValue.length || filterValue.some((v, i) => v[1] != elValue[i]); - } - else { - return filterValue ? filterValue != elValue : elValue; + // New filter + if (!filter) { + $saveButton.prop('disabled', false); + $deleteButton.prop('disabled', true); + return; } - }); - const defaultStatusChanged = $isDefaultCheckbox.prop('checked') != (defaultFilter === $select.val()); - $saveButton.prop('disabled', !defaultStatusChanged && (filter.system || !hasChanges)); - } - - async function initializeSelect() { - defaultFilter = await QPixel.defaultFilter(categoryId); - $isDefaultCheckbox.prop('checked', defaultFilter === $select.val()); - const filters = await QPixel.filters(); - - function template(option) { - if (option.id == '') { return 'Default'; } - - const filter = filters[option.id]; - const name = `${option.text}`; - const systemIndicator = filter?.system - ? ' (System)' - : ''; - const newIndicator = !filter - ? ' (New)' - : ''; - return $(name + systemIndicator + newIndicator); + + // Not a new filter + $deleteButton.prop('disabled', filter.system); + + const hasChanges = [...$formFilters].some((el) => { + const filterValue = filter[el.dataset.name]; + let elValue = /** @type {string | undefined[]} */ ($(el).val()); + if (filterValue?.constructor == Array) { + elValue = elValue ?? []; + return filterValue.length != elValue.length || filterValue.some((v, i) => v[1] != elValue[i]); + } else { + return filterValue ? filterValue != elValue : elValue; + } + }); + const defaultStatusChanged = $isDefaultCheckbox.prop('checked') != (defaultFilter === $select.val()); + $saveButton.prop('disabled', !defaultStatusChanged && (filter.system || !hasChanges)); } - // Clear out any old options - $select.children().filter((_, /** @type{HTMLOptionElement} */ option) => { - return option.value && !filters[option.value]; - }).detach(); + async function initializeSelect() { + defaultFilter = await QPixel.defaultFilter(categoryId); + $isDefaultCheckbox.prop('checked', defaultFilter === $select.val()); + const filters = await QPixel.filters(); - $select.select2({ - data: Object.keys(filters).map((filterName) => { - return { - id: filterName, - text: filterName + function template(option) { + if (option.id == '') { + return 'Default'; } - }), - tags: true, - templateResult: template, - templateSelection: template - }); - $select.on('select2:select', /** @type {(event: Select2.Event) => void} */ (async (evt) => { - const filterName = evt.params.data.id; - const preset = filters[filterName]; + const filter = filters[option.id]; + const name = `${option.text}`; + const systemIndicator = filter?.system ? ' (System)' : ''; + const newIndicator = !filter ? ' (New)' : ''; + return $(name + systemIndicator + newIndicator); + } - $isDefaultCheckbox.prop('checked', defaultFilter === $select.val()); + // Clear out any old options + $select + .children() + .filter((_, /** @type{HTMLOptionElement} */ option) => { + return option.value && !filters[option.value]; + }) + .detach(); + + $select.select2({ + data: Object.keys(filters).map((filterName) => { + return { + id: filterName, + text: filterName + }; + }), + tags: true, + templateResult: template, + templateSelection: template + }); + + $select.on( + 'select2:select', + /** @type {(event: Select2.Event) => void} */ ( + async (evt) => { + const filterName = evt.params.data.id; + const preset = filters[filterName]; + + $isDefaultCheckbox.prop('checked', defaultFilter === $select.val()); + computeEnables(); + + // Name is not one of the presets, i.e user is creating a new preset + if (!preset) { + return; + } + + for (const [name, value] of Object.entries(preset)) { + const $el = $form.find(`.form--filter[data-name=${name}]`); + if (value?.constructor == Array) { + $el.val(null); + for (const val of value) { + $el.append(new Option(val[0], val[1].toString(), false, true)); + } + $el.trigger('change'); + } else { + $el.val(/** @type {string} */ (value)).trigger('change'); + } + } + } + ) + ); computeEnables(); + } - // Name is not one of the presets, i.e user is creating a new preset - if (!preset) { - return; - } + initializeSelect(); - for (const [name, value] of Object.entries(preset)) { - const $el = $form.find(`.form--filter[data-name=${name}]`); - if (value?.constructor == Array) { - $el.val(null); - for (const val of value) { - $el.append(new Option(val[0], val[1].toString(), false, true)); - } - $el.trigger('change'); - } - else { - $el.val(/** @type {string} */ (value)).trigger('change'); - } - } - })); - computeEnables(); - } + // Enable saving when the filter is changed + $formFilters.on('change', computeEnables); + $isDefaultCheckbox.on('change', computeEnables); - initializeSelect(); + async function saveFilter() { + if (!$form[0].reportValidity()) { + return; + } - // Enable saving when the filter is changed - $formFilters.on('change', computeEnables); - $isDefaultCheckbox.on('change', computeEnables); + const filter = /** @type {QPixelFilter} */ ({}); - async function saveFilter() { - if (!$form[0].reportValidity()) { return; } + for (const el of $formFilters) { + filter[el.dataset.name] = $(el).val(); + } - const filter = /** @type {QPixelFilter} */({}); + await QPixel.setFilter($select.val()?.toString(), filter, categoryId, $isDefaultCheckbox.prop('checked')); + defaultFilter = await QPixel.defaultFilter(categoryId); - for (const el of $formFilters) { - filter[el.dataset.name] = $(el).val(); + // Reinitialize to get new options + await initializeSelect(); } - await QPixel.setFilter($select.val()?.toString(), filter, categoryId, $isDefaultCheckbox.prop('checked')); - defaultFilter = await QPixel.defaultFilter(categoryId); + $saveButton.on('click', saveFilter); - // Reinitialize to get new options - await initializeSelect(); - } - - $saveButton.on('click', saveFilter); + function clear() { + $select.val(null).trigger('change'); + $form.find('.form--filter').val(null).trigger('change'); + $isDefaultCheckbox.prop('checked', false); + computeEnables(); + } - function clear() { - $select.val(null).trigger('change'); - $form.find('.form--filter').val(null).trigger('change'); - $isDefaultCheckbox.prop('checked', false); - computeEnables(); - } + $deleteButton?.on('click', async (_evt) => { + if (confirm(`Are you sure you want to delete ${$select.val()}?`)) { + await QPixel.deleteFilter($select.val()?.toString()); + // Reinitialize to get new options + await initializeSelect(); + clear(); + } + }); - $deleteButton?.on('click', async (_evt) => { - if (confirm(`Are you sure you want to delete ${$select.val()}?`)) { - await QPixel.deleteFilter($select.val()?.toString()); - // Reinitialize to get new options - await initializeSelect(); - clear(); - } + $form.find('.filter-clear').on('click', clear); }); - - $form.find('.filter-clear').on('click', clear); - }); }); From 408539281222746a4a3adf8d5fe22858b2b6e881 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:46:41 +0300 Subject: [PATCH 06/10] added types for Prettier & plugin needed for stroustrup brace style --- package-lock.json | 90 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 92 insertions(+) diff --git a/package-lock.json b/package-lock.json index 8e19e8c1d..22257c281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,9 @@ "name": "qpixel", "devDependencies": { "@types/jquery": "^3.5.32", + "@types/prettier": "2.7.3", "@types/select2": "^4.0.63", + "prettier-plugin-brace-style": "0.8.1", "typescript": "5.6.3" } }, @@ -21,6 +23,13 @@ "@types/sizzle": "*" } }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/select2": { "version": "4.0.63", "resolved": "https://registry.npmjs.org/@types/select2/-/select2-4.0.63.tgz", @@ -37,6 +46,49 @@ "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", "dev": true }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-brace-style": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-brace-style/-/prettier-plugin-brace-style-0.8.1.tgz", + "integrity": "sha512-nhyuc8ETHk/TDNegbH3t6xq+WRVsAqIRLfT3sqZZO+PU8dSQ8dZVhw9FBazFF7FwR0masRx/TbrSdS66IC37Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "zod": "3.22.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "prettier": "^3", + "prettier-plugin-astro": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", @@ -49,6 +101,16 @@ "engines": { "node": ">=14.17" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -61,6 +123,12 @@ "@types/sizzle": "*" } }, + "@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, "@types/select2": { "version": "4.0.63", "resolved": "https://registry.npmjs.org/@types/select2/-/select2-4.0.63.tgz", @@ -76,11 +144,33 @@ "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", "dev": true }, + "prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "peer": true + }, + "prettier-plugin-brace-style": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-brace-style/-/prettier-plugin-brace-style-0.8.1.tgz", + "integrity": "sha512-nhyuc8ETHk/TDNegbH3t6xq+WRVsAqIRLfT3sqZZO+PU8dSQ8dZVhw9FBazFF7FwR0masRx/TbrSdS66IC37Zw==", + "dev": true, + "requires": { + "zod": "3.22.4" + } + }, "typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true + }, + "zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true } } } diff --git a/package.json b/package.json index c3e3860bf..69730e973 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ }, "devDependencies": { "@types/jquery": "^3.5.32", + "@types/prettier": "2.7.3", "@types/select2": "^4.0.63", + "prettier-plugin-brace-style": "0.8.1", "typescript": "5.6.3" } } From 8a093dd2273159831207cbe431ea2142c6bbe903 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:51:20 +0300 Subject: [PATCH 07/10] added Prettier config to enable strousrup brace style formatting --- .prettierrc.mjs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .prettierrc.mjs diff --git a/.prettierrc.mjs b/.prettierrc.mjs new file mode 100644 index 000000000..607270d42 --- /dev/null +++ b/.prettierrc.mjs @@ -0,0 +1,12 @@ +/** + * @type {import('prettier').Config} + */ +const config = { + braceStyle: "stroustrup", + plugins: ["prettier-plugin-brace-style"], + printWidth: 120, + singleQuote: true, + tabWidth: 2, +}; + +export default config; From 19eeb9bf71aabff03e1ac4a764b90b74e616d4e7 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:51:42 +0300 Subject: [PATCH 08/10] relinted filters JS asset to match our brace styling --- app/assets/javascripts/filters.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js index 274c41dbf..481cff274 100644 --- a/app/assets/javascripts/filters.js +++ b/app/assets/javascripts/filters.js @@ -41,7 +41,8 @@ document.addEventListener('DOMContentLoaded', () => { if (filterValue?.constructor == Array) { elValue = elValue ?? []; return filterValue.length != elValue.length || filterValue.some((v, i) => v[1] != elValue[i]); - } else { + } + else { return filterValue ? filterValue != elValue : elValue; } }); @@ -78,12 +79,12 @@ document.addEventListener('DOMContentLoaded', () => { data: Object.keys(filters).map((filterName) => { return { id: filterName, - text: filterName + text: filterName, }; }), tags: true, templateResult: template, - templateSelection: template + templateSelection: template, }); $select.on( @@ -109,12 +110,13 @@ document.addEventListener('DOMContentLoaded', () => { $el.append(new Option(val[0], val[1].toString(), false, true)); } $el.trigger('change'); - } else { + } + else { $el.val(/** @type {string} */ (value)).trigger('change'); } } } - ) + ), ); computeEnables(); } From 4f202e58cc75e02537cbd86ee04c208c3acb1b9b Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 06:53:02 +0300 Subject: [PATCH 09/10] removed unnecessary default filter requests on pages that don't have a category set --- app/assets/javascripts/filters.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js index 481cff274..1cc3d21ba 100644 --- a/app/assets/javascripts/filters.js +++ b/app/assets/javascripts/filters.js @@ -8,7 +8,7 @@ document.addEventListener('DOMContentLoaded', () => { const $saveButton = $form.find('.filter-save'); const $isDefaultCheckbox = $form.find('.filter-is-default'); const categoryId = $isDefaultCheckbox.val()?.toString(); - let defaultFilter = await QPixel.defaultFilter(categoryId); + let defaultFilter = categoryId ? await QPixel.defaultFilter(categoryId) : null; const $deleteButton = $form.find('.filter-delete'); // Enables/Disables Save & Delete buttons programatically @@ -51,7 +51,7 @@ document.addEventListener('DOMContentLoaded', () => { } async function initializeSelect() { - defaultFilter = await QPixel.defaultFilter(categoryId); + defaultFilter = categoryId ? await QPixel.defaultFilter(categoryId) : null; $isDefaultCheckbox.prop('checked', defaultFilter === $select.val()); const filters = await QPixel.filters(); @@ -139,7 +139,8 @@ document.addEventListener('DOMContentLoaded', () => { } await QPixel.setFilter($select.val()?.toString(), filter, categoryId, $isDefaultCheckbox.prop('checked')); - defaultFilter = await QPixel.defaultFilter(categoryId); + + defaultFilter = categoryId ? await QPixel.defaultFilter(categoryId) : null; // Reinitialize to get new options await initializeSelect(); From edb7bd258a73c7a2b492c272dff3a63f462b17bd Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 12 Sep 2025 07:26:14 +0300 Subject: [PATCH 10/10] added tests for the default_filter action --- test/controllers/users_controller_test.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 055032bad..e257ae957 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -553,6 +553,18 @@ class UsersControllerTest < ActionController::TestCase end end + test 'default_filter should correctly respond to missing category' do + sign_in users(:standard_user) + try_default_filter(nil) + assert_response(:bad_request) + end + + test 'default_filter should correctly get default category filters' do + sign_in users(:standard_user) + try_default_filter(categories(:main)) + assert_json_success + end + private def create_other_user @@ -563,6 +575,13 @@ def create_other_user other_user end + def try_default_filter(category) + get :default_filter, params: { + category: category&.id, + format: :json + } + end + def try_save_filter(**opts) filter = { name: 'test filter' }.merge(opts) post :set_filter, params: filter.merge({ format: :json })