discourse/plugins/discourse-gamification/test/javascripts/integration/components/admin-edit-leaderboard-test.gjs
Rafael dos Santos Silva 848c19ae48
FEATURE: Per-leaderboard scorable weights and category filters (#39062)
## Summary

- Moves scorable weight configuration and category filtering from global
site settings to individual leaderboards
- Each leaderboard can override any of the 15 score weight values and
specify its own scorable categories
- Score calculation now runs per leaderboard into a new
`gamification_leaderboard_scores` table
- Admin UI gains a "Scoring configuration" section on the leaderboard
edit form

## Details

Previously all leaderboards shared the same pre-calculated scores from
global site settings. This made it impossible to have e.g. a
"posts-only" leaderboard alongside a "likes-focused" one.

Now each leaderboard can optionally override:
- **Score weights**: Set per-action point values (empty = inherit global
default, 0 = disabled)
- **Scorable categories**: Restrict which categories count toward
scoring (empty = inherit global setting)

The old `gamification_scores` table is no longer written to — a
post-deploy migration to drop it will follow separately.

## Test plan

- [ ] Create a leaderboard with default scoring — scores match current
behavior
- [ ] Create a leaderboard with custom weights (e.g. `post_created` =
10, all others empty) — only overridden weights differ
- [ ] Set a weight to 0 — that scorable is disabled for the leaderboard
- [ ] Set per-leaderboard categories — only those categories count
- [ ] Clear categories — inherits global `scorable_categories` setting
- [ ] Existing leaderboards with no overrides behave identically after
upgrade
- [ ] Directory scores and user card scores work correctly (read from
default leaderboard)
- [ ] `bin/rspec plugins/discourse-gamification/spec/` — 120 examples, 0
failures
2026-04-23 11:23:01 -03:00

50 lines
1.4 KiB
Text

import { render } from "@ember/test-helpers";
import { module, test } from "qunit";
import sinon from "sinon";
import Category from "discourse/models/category";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AdminEditLeaderboard from "discourse/plugins/discourse-gamification/admin/components/admin-edit-leaderboard";
module(
"Discourse Gamification | Integration | Component | admin-edit-leaderboard",
function (hooks) {
setupRenderingTest(hooks);
hooks.afterEach(function () {
sinon.restore();
});
test("loads selected scorable categories asynchronously", async function (assert) {
const category = Category.findById(1001);
this.site.lazy_load_categories = true;
sinon.stub(Category, "findById").returns(null);
const asyncFindByIdsStub = sinon
.stub(Category, "asyncFindByIds")
.resolves([category]);
this.leaderboard = {
id: 1,
name: "Leaderboard",
fromDate: null,
toDate: null,
includedGroupsIds: [],
excludedGroupsIds: [],
visibleToGroupsIds: [],
defaultPeriod: 0,
periodFilterDisabled: false,
scoreOverrides: null,
scorableCategoryIds: [1001],
};
await render(
<template>
<AdminEditLeaderboard @leaderboard={{this.leaderboard}} />
</template>
);
assert.true(asyncFindByIdsStub.calledWith([1001]));
});
}
);