gatsby-core-theme 44.15.2 → 44.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/package.json +1 -1
- package/src/constants/trackerTypePerSite.mjs +13 -0
- package/src/helpers/generators.mjs +7 -3
- package/src/helpers/generators.test.js +42 -0
- package/src/helpers/toplist.mjs +142 -0
- package/src/helpers/toplist.test.js +288 -0
- package/src/helpers/tracker.mjs +1 -1
- package/src/resolver/modules.mjs +39 -81
- /package/src/helpers/{cookies.js → cookies.mjs} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
# [44.16.0](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.15.3...v44.16.0) (2026-02-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* guard toplists access in processTopListModule to prevent crash on undefined ([0fd53b3](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/0fd53b3f96cede6120838777707ae0a993284b43))
|
|
7
|
+
* include operators on game toplist ([fcaae16](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/fcaae16634f3dc8879f2818eb8e50fb14451a495))
|
|
8
|
+
* pass single operator for game toplist item ([181f031](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/181f031276fa7b16d3d58b8b240f754901f6270f))
|
|
9
|
+
* process game top list ([c7f494a](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/c7f494aefc29bb82c6c98dc013bdfe58a9befa9d))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Code Refactoring
|
|
13
|
+
|
|
14
|
+
* extract toplist helpers from modules.mjs into helpers/toplist.mjs ([3727ed8](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/3727ed863baa2cfaccdfecba8750ebc3dbbc19d8))
|
|
15
|
+
* process top list ([e2c4589](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/e2c45898b74b1412125464b4c230aa1be060ed32))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
* Merge branch 'en-379-game-toplist' into 'master' ([5f8672c](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/5f8672cbcba7853bee36fa6aab68e0a41d2152f0))
|
|
19
|
+
* Merge remote-tracking branch 'origin' into en-379-game-toplist ([f6b2f05](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/f6b2f05c9c598f687af74c420d6ccee96eec3582))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* process game toplist ([2f7944f](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/2f7944f612dd3e970726c0a5e82e5027e9360922))
|
|
25
|
+
|
|
26
|
+
## [44.15.3](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.15.2...v44.15.3) (2026-02-26)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* add also meutimao ([cbe7293](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/cbe729308e6a84760e7f3b93213600f1a8c83968))
|
|
32
|
+
* improve the tracker type and make possible to edit for specific sites ([a3f2e17](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/a3f2e1776c8c1ce14a05fba06f375ad51117bdce))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
* Merge branch 'tracker-type-built' into 'master' ([790c754](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/790c7543bbd2532f5ab1cac6fd8d6f34b38f8461))
|
|
36
|
+
|
|
1
37
|
## [44.15.2](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.15.1...v44.15.2) (2026-02-26)
|
|
2
38
|
|
|
3
39
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
'odia.ig.com.br': {
|
|
3
|
+
'casino': ['type', "[cassino]"],
|
|
4
|
+
'sportsbook': ['type', "[esportes]"],
|
|
5
|
+
},
|
|
6
|
+
'meutimao.com.br': {
|
|
7
|
+
'casino': ['type', "[cassino]"],
|
|
8
|
+
'sportsbook': ['type', "[esportes]"],
|
|
9
|
+
},
|
|
10
|
+
default: {
|
|
11
|
+
casino: ['type,', ''],
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import generatorsConstant from "../constants/generators.mjs";
|
|
2
|
+
import trackerTypePerSite from "../constants/trackerTypePerSite.mjs";
|
|
2
3
|
|
|
3
4
|
export function generateTrackerLink(operator, trackerType, provider = false) {
|
|
4
5
|
let defaultTrackerFormat = provider
|
|
@@ -8,11 +9,14 @@ export function generateTrackerLink(operator, trackerType, provider = false) {
|
|
|
8
9
|
? process.env.PROVIDER_TRACKER_LINK_FORMAT_NON_MAIN
|
|
9
10
|
: process.env.TRACKER_LINK_FORMAT_NON_MAIN;
|
|
10
11
|
|
|
12
|
+
const replaceTrackerFormat = trackerTypePerSite[process.env.GATSBY_SITE_NAME] || trackerTypePerSite.default;
|
|
13
|
+
|
|
11
14
|
if (defaultTrackerFormat && trackerFormat) {
|
|
12
|
-
if
|
|
13
|
-
defaultTrackerFormat = defaultTrackerFormat.replace(
|
|
14
|
-
trackerFormat = trackerFormat.replace(
|
|
15
|
+
if(replaceTrackerFormat?.[operator?.type]){
|
|
16
|
+
defaultTrackerFormat = defaultTrackerFormat.replace(replaceTrackerFormat[operator.type][0], replaceTrackerFormat[operator.type][1]);
|
|
17
|
+
trackerFormat = trackerFormat.replace(replaceTrackerFormat[operator.type][0], replaceTrackerFormat[operator.type][1]);
|
|
15
18
|
}
|
|
19
|
+
|
|
16
20
|
const linkFormatArray =
|
|
17
21
|
trackerType === "main"
|
|
18
22
|
? defaultTrackerFormat.split(",")
|
|
@@ -31,6 +31,48 @@ describe("Generate Tracker Link Helper", () => {
|
|
|
31
31
|
);
|
|
32
32
|
expect(trackerLink).toEqual("/no/visit/bet365");
|
|
33
33
|
});
|
|
34
|
+
test("sportsbook type is shown as-is for default site (non_main)", () => {
|
|
35
|
+
const trackerLink = generateTrackerLink(
|
|
36
|
+
{ type: "sportsbook", short_name: "bet365" },
|
|
37
|
+
"non_main"
|
|
38
|
+
);
|
|
39
|
+
expect(trackerLink).toEqual("/no/visit/bet365/sportsbook/non_main");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("Generate Tracker Link Helper - Site Type Translations", () => {
|
|
44
|
+
const originalSiteName = process.env.GATSBY_SITE_NAME;
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
process.env.GATSBY_SITE_NAME = originalSiteName;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("casino type is translated for translated site (non_main)", () => {
|
|
51
|
+
process.env.GATSBY_SITE_NAME = "odia.ig.com.br";
|
|
52
|
+
const trackerLink = generateTrackerLink(
|
|
53
|
+
{ type: "casino", short_name: "bet365" },
|
|
54
|
+
"non_main"
|
|
55
|
+
);
|
|
56
|
+
expect(trackerLink).toEqual("/no/visit/bet365/cassino/non_main");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("sportsbook type is translated for translated site (non_main)", () => {
|
|
60
|
+
process.env.GATSBY_SITE_NAME = "odia.ig.com.br";
|
|
61
|
+
const trackerLink = generateTrackerLink(
|
|
62
|
+
{ type: "sportsbook", short_name: "bet365" },
|
|
63
|
+
"non_main"
|
|
64
|
+
);
|
|
65
|
+
expect(trackerLink).toEqual("/no/visit/bet365/esportes/non_main");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("main tracker link is unaffected by type translation", () => {
|
|
69
|
+
process.env.GATSBY_SITE_NAME = "odia.ig.com.br";
|
|
70
|
+
const trackerLink = generateTrackerLink(
|
|
71
|
+
{ type: "casino", short_name: "bet365" },
|
|
72
|
+
"main"
|
|
73
|
+
);
|
|
74
|
+
expect(trackerLink).toEqual("/no/visit/bet365");
|
|
75
|
+
});
|
|
34
76
|
});
|
|
35
77
|
|
|
36
78
|
describe("Generate Placeholders String", () => {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
3
|
+
import loadash from "lodash/index.js";
|
|
4
|
+
import { stripDomainAndLeadingSlash } from "./path.mjs";
|
|
5
|
+
import { sanitizeOperatorData } from "../resolver/operators.mjs";
|
|
6
|
+
import { sanitizegameData } from "../resolver/games.mjs";
|
|
7
|
+
import buildToplistFilter from "../resolver/build-toplist-filter.mjs";
|
|
8
|
+
|
|
9
|
+
const { cloneDeep } = loadash;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} item - Raw toplist item from DMS (operator or game)
|
|
13
|
+
* @param {Object|null} itemRelation - Resolved relation data for the item
|
|
14
|
+
* @param {Array<Object>} itemPages - Pages matching this relation
|
|
15
|
+
* @param {Object} ctx - Shared processing context
|
|
16
|
+
* @param {boolean} ctx.filterEnabled - Whether filter mode is active
|
|
17
|
+
* @param {Object} ctx.filters - Filter accumulator object
|
|
18
|
+
* @param {Object} ctx.data - Full relations data (payment methods, etc.)
|
|
19
|
+
* @param {Object|null} ctx.toplists - Toplists lookup map
|
|
20
|
+
* @param {string|undefined} ctx.toplistLabel - Label from the parent toplist item
|
|
21
|
+
* @returns {Object|null} Sanitized toplist item or null if no relation
|
|
22
|
+
*/
|
|
23
|
+
export function processToplistItem(item, itemRelation, itemPages, ctx) {
|
|
24
|
+
if (!itemRelation) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
itemRelation.ribbons =
|
|
29
|
+
item.ribbon_ids && item.ribbon_ids.length === 0
|
|
30
|
+
? itemRelation.ribbons
|
|
31
|
+
: item.ribbon_ids;
|
|
32
|
+
|
|
33
|
+
itemRelation.selling_points =
|
|
34
|
+
item.selling_points && item.selling_points.length === 0
|
|
35
|
+
? itemRelation.selling_points
|
|
36
|
+
: item.selling_points;
|
|
37
|
+
|
|
38
|
+
itemRelation.updated_at = item.updated_at;
|
|
39
|
+
itemRelation.toplist_bonus = item.bonus_name;
|
|
40
|
+
itemRelation.toplist_item_tracker_name = item.tracker_name;
|
|
41
|
+
|
|
42
|
+
const reviewLink =
|
|
43
|
+
process.env.DMS_REVIEW_LINKS_ENABLED === "true"
|
|
44
|
+
? stripDomainAndLeadingSlash(itemRelation?.review_link)
|
|
45
|
+
: itemPages[0]?.path;
|
|
46
|
+
|
|
47
|
+
const authorTitle = itemPages[0]?.author?.author_title;
|
|
48
|
+
const clone = item.game_id
|
|
49
|
+
? sanitizegameData(cloneDeep(itemRelation), itemPages, ["operators"])
|
|
50
|
+
: sanitizeOperatorData(
|
|
51
|
+
cloneDeep(itemRelation),
|
|
52
|
+
itemPages,
|
|
53
|
+
ctx.data,
|
|
54
|
+
ctx.toplistLabel
|
|
55
|
+
);
|
|
56
|
+
const socialAuthorLinks = [
|
|
57
|
+
"facebook_profile",
|
|
58
|
+
"twitter_profile",
|
|
59
|
+
"instagram_profile",
|
|
60
|
+
"linkedin_profile",
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
if (ctx.filterEnabled) {
|
|
64
|
+
buildToplistFilter(ctx.filters, clone);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return Object.assign(clone, {
|
|
68
|
+
review_link: reviewLink,
|
|
69
|
+
author_title: authorTitle,
|
|
70
|
+
author_same_as:
|
|
71
|
+
itemPages[0]?.author &&
|
|
72
|
+
socialAuthorLinks
|
|
73
|
+
.map((socialLink) => itemPages[0]?.author?.[socialLink])
|
|
74
|
+
.filter((socialLink) => socialLink),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {Object} item - Raw operator toplist item with operator_id
|
|
80
|
+
* @param {Object} ctx - Shared processing context
|
|
81
|
+
* @param {Object} ctx.relations - All relations keyed by type (operator, game, etc.)
|
|
82
|
+
* @param {Object} ctx.pages - Pages grouped by type
|
|
83
|
+
* @param {string|undefined} ctx.type - Operator type filter (e.g. "casino")
|
|
84
|
+
* @param {string} ctx.market - Market short code
|
|
85
|
+
* @param {boolean} ctx.filterEnabled
|
|
86
|
+
* @param {Object} ctx.filters
|
|
87
|
+
* @param {Object} ctx.data
|
|
88
|
+
* @param {Object|null} ctx.toplists
|
|
89
|
+
* @param {string|undefined} ctx.toplistLabel
|
|
90
|
+
* @returns {Object|null} Sanitized operator item or null
|
|
91
|
+
*/
|
|
92
|
+
export function processOperatorToplist(item, ctx) {
|
|
93
|
+
const operatorRelation = cloneDeep(
|
|
94
|
+
Object.values(ctx.relations.operator).find(
|
|
95
|
+
(operator) =>
|
|
96
|
+
operator.operator_id === (item.operator_id || item.id) &&
|
|
97
|
+
operator.market === ctx.market &&
|
|
98
|
+
(ctx.type ? ctx.type === operator.type : true)
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const operatorPage =
|
|
103
|
+
ctx.pages?.operator && operatorRelation
|
|
104
|
+
? ctx.pages.operator.filter(
|
|
105
|
+
(page) =>
|
|
106
|
+
page.relation?.operator_id === operatorRelation.operator_id &&
|
|
107
|
+
page.relation?.type === operatorRelation.type
|
|
108
|
+
)
|
|
109
|
+
: [];
|
|
110
|
+
|
|
111
|
+
return processToplistItem(item, operatorRelation, operatorPage, ctx);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {Object} item - Raw game toplist item with game_id and operators array
|
|
116
|
+
* @param {Object} ctx - Same context as processOperatorToplist
|
|
117
|
+
* @returns {Object|null} Sanitized game item or null
|
|
118
|
+
*/
|
|
119
|
+
export function processGameToplist(item, ctx) {
|
|
120
|
+
const relation = cloneDeep(
|
|
121
|
+
Object.values(ctx.relations.game).find(
|
|
122
|
+
(game) => game.game_id === item.game_id
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
const gameOperators = (item.operators ?? [])
|
|
126
|
+
.map((operator) => processOperatorToplist(operator, ctx))
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
const gamePage = ctx.pages?.game
|
|
129
|
+
? ctx.pages.game.filter(
|
|
130
|
+
(page) =>
|
|
131
|
+
page.relation?.game_id === relation?.game_id &&
|
|
132
|
+
page.relation?.market === ctx.market
|
|
133
|
+
)
|
|
134
|
+
: [];
|
|
135
|
+
|
|
136
|
+
return processToplistItem(
|
|
137
|
+
item,
|
|
138
|
+
{ ...relation, operators: gameOperators?.[0] },
|
|
139
|
+
gamePage,
|
|
140
|
+
ctx
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import loadash from "lodash/index.js";
|
|
2
|
+
import {
|
|
3
|
+
processToplistItem,
|
|
4
|
+
processOperatorToplist,
|
|
5
|
+
processGameToplist,
|
|
6
|
+
} from "./toplist.mjs";
|
|
7
|
+
import { relationData } from "~tests/factories/modules/toplist.factory";
|
|
8
|
+
|
|
9
|
+
const { cloneDeep } = loadash;
|
|
10
|
+
|
|
11
|
+
jest.mock("../resolver/build-toplist-filter.mjs", () => ({
|
|
12
|
+
__esModule: true,
|
|
13
|
+
default: jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
// eslint-disable-next-line import/first
|
|
16
|
+
import buildToplistFilter from "../resolver/build-toplist-filter.mjs";
|
|
17
|
+
|
|
18
|
+
describe("processToplistItem", () => {
|
|
19
|
+
const baseCtx = {
|
|
20
|
+
filterEnabled: false,
|
|
21
|
+
filters: {},
|
|
22
|
+
data: relationData,
|
|
23
|
+
toplists: null,
|
|
24
|
+
toplistLabel: undefined,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
test("returns null when itemRelation is null", () => {
|
|
28
|
+
const result = processToplistItem({ operator_id: 1 }, null, [], baseCtx);
|
|
29
|
+
expect(result).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("returns null when itemRelation is undefined", () => {
|
|
33
|
+
const result = processToplistItem(
|
|
34
|
+
{ operator_id: 1 },
|
|
35
|
+
undefined,
|
|
36
|
+
[],
|
|
37
|
+
baseCtx
|
|
38
|
+
);
|
|
39
|
+
expect(result).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns sanitized operator item with correct fields", () => {
|
|
43
|
+
const item = {
|
|
44
|
+
operator_id: 6342,
|
|
45
|
+
selling_points: ["classicslots", "bitcoin", "bingo"],
|
|
46
|
+
updated_at: "2025-01-14 11:04:49",
|
|
47
|
+
bonus_name: "Welcome Bonus",
|
|
48
|
+
tracker_name: "main_tracker",
|
|
49
|
+
};
|
|
50
|
+
const itemRelation = cloneDeep(relationData.operator[6342]);
|
|
51
|
+
const pages = [{ path: "en/slotum", author: { author_title: "John" } }];
|
|
52
|
+
|
|
53
|
+
const result = processToplistItem(item, itemRelation, pages, baseCtx);
|
|
54
|
+
|
|
55
|
+
expect(result).not.toBeNull();
|
|
56
|
+
expect(result.review_link).toBe("en/slotum");
|
|
57
|
+
expect(result.author_title).toBe("John");
|
|
58
|
+
expect(result.short_name).toBe("slotum");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("preserves original ribbons when ribbon_ids is empty array", () => {
|
|
62
|
+
const originalRibbons = [1, 2, 3];
|
|
63
|
+
const item = {
|
|
64
|
+
operator_id: 6342,
|
|
65
|
+
ribbon_ids: [],
|
|
66
|
+
updated_at: "2025-01-14",
|
|
67
|
+
};
|
|
68
|
+
const itemRelation = cloneDeep(relationData.operator[6342]);
|
|
69
|
+
itemRelation.ribbons = originalRibbons;
|
|
70
|
+
|
|
71
|
+
processToplistItem(item, itemRelation, [], baseCtx);
|
|
72
|
+
|
|
73
|
+
expect(itemRelation.ribbons).toEqual(originalRibbons);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("overrides ribbons when ribbon_ids has values", () => {
|
|
77
|
+
const item = {
|
|
78
|
+
operator_id: 6342,
|
|
79
|
+
ribbon_ids: [10, 20],
|
|
80
|
+
updated_at: "2025-01-14",
|
|
81
|
+
};
|
|
82
|
+
const itemRelation = cloneDeep(relationData.operator[6342]);
|
|
83
|
+
itemRelation.ribbons = [1, 2, 3];
|
|
84
|
+
|
|
85
|
+
processToplistItem(item, itemRelation, [], baseCtx);
|
|
86
|
+
|
|
87
|
+
expect(itemRelation.ribbons).toEqual([10, 20]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("preserves original selling_points when selling_points is empty array", () => {
|
|
91
|
+
const originalSP = ["point1", "point2"];
|
|
92
|
+
const item = {
|
|
93
|
+
operator_id: 6342,
|
|
94
|
+
selling_points: [],
|
|
95
|
+
updated_at: "2025-01-14",
|
|
96
|
+
};
|
|
97
|
+
const itemRelation = cloneDeep(relationData.operator[6342]);
|
|
98
|
+
itemRelation.selling_points = originalSP;
|
|
99
|
+
|
|
100
|
+
processToplistItem(item, itemRelation, [], baseCtx);
|
|
101
|
+
|
|
102
|
+
expect(itemRelation.selling_points).toEqual(originalSP);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("calls buildToplistFilter when filterEnabled is true", () => {
|
|
106
|
+
buildToplistFilter.mockClear();
|
|
107
|
+
const ctx = { ...baseCtx, filterEnabled: true, filters: { test: true } };
|
|
108
|
+
const item = {
|
|
109
|
+
operator_id: 6342,
|
|
110
|
+
selling_points: ["a"],
|
|
111
|
+
updated_at: "2025-01-14",
|
|
112
|
+
};
|
|
113
|
+
const itemRelation = cloneDeep(relationData.operator[6342]);
|
|
114
|
+
|
|
115
|
+
processToplistItem(item, itemRelation, [], ctx);
|
|
116
|
+
|
|
117
|
+
expect(buildToplistFilter).toHaveBeenCalledTimes(1);
|
|
118
|
+
expect(buildToplistFilter).toHaveBeenCalledWith(
|
|
119
|
+
ctx.filters,
|
|
120
|
+
expect.any(Object)
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("does not call buildToplistFilter when filterEnabled is false", () => {
|
|
125
|
+
buildToplistFilter.mockClear();
|
|
126
|
+
const item = {
|
|
127
|
+
operator_id: 6342,
|
|
128
|
+
selling_points: ["a"],
|
|
129
|
+
updated_at: "2025-01-14",
|
|
130
|
+
};
|
|
131
|
+
const itemRelation = cloneDeep(relationData.operator[6342]);
|
|
132
|
+
|
|
133
|
+
processToplistItem(item, itemRelation, [], baseCtx);
|
|
134
|
+
|
|
135
|
+
expect(buildToplistFilter).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("processOperatorToplist", () => {
|
|
140
|
+
const baseCtx = {
|
|
141
|
+
relations: relationData,
|
|
142
|
+
pages: undefined,
|
|
143
|
+
type: "casino",
|
|
144
|
+
market: undefined,
|
|
145
|
+
filterEnabled: false,
|
|
146
|
+
filters: {},
|
|
147
|
+
data: relationData,
|
|
148
|
+
toplists: null,
|
|
149
|
+
toplistLabel: undefined,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
test("finds correct operator relation by operator_id and type", () => {
|
|
153
|
+
const item = {
|
|
154
|
+
operator_id: 6342,
|
|
155
|
+
selling_points: ["a", "b", "c"],
|
|
156
|
+
updated_at: "2025-01-14",
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const result = processOperatorToplist(item, baseCtx);
|
|
160
|
+
|
|
161
|
+
expect(result).not.toBeNull();
|
|
162
|
+
expect(result.short_name).toBe("slotum");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("returns null when no matching operator relation exists", () => {
|
|
166
|
+
const item = {
|
|
167
|
+
operator_id: 99999,
|
|
168
|
+
updated_at: "2025-01-14",
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const result = processOperatorToplist(item, baseCtx);
|
|
172
|
+
|
|
173
|
+
expect(result).toBeNull();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("falls back to item.id when item.operator_id is missing", () => {
|
|
177
|
+
const item = {
|
|
178
|
+
id: 6342,
|
|
179
|
+
selling_points: ["a"],
|
|
180
|
+
updated_at: "2025-01-14",
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const result = processOperatorToplist(item, baseCtx);
|
|
184
|
+
|
|
185
|
+
expect(result).not.toBeNull();
|
|
186
|
+
expect(result.short_name).toBe("slotum");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("matches operator pages by operator_id and type", () => {
|
|
190
|
+
const ctx = {
|
|
191
|
+
...baseCtx,
|
|
192
|
+
pages: {
|
|
193
|
+
operator: [
|
|
194
|
+
{
|
|
195
|
+
relation_id: 6342,
|
|
196
|
+
path: "en/slotum",
|
|
197
|
+
relation: { operator_id: 6342, type: "casino" },
|
|
198
|
+
author: { author_title: "Editor" },
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
const item = {
|
|
204
|
+
operator_id: 6342,
|
|
205
|
+
selling_points: ["a"],
|
|
206
|
+
updated_at: "2025-01-14",
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const result = processOperatorToplist(item, ctx);
|
|
210
|
+
|
|
211
|
+
expect(result.review_link).toBe("en/slotum");
|
|
212
|
+
expect(result.author_title).toBe("Editor");
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("processGameToplist", () => {
|
|
217
|
+
const gameRelations = {
|
|
218
|
+
...relationData,
|
|
219
|
+
game: {
|
|
220
|
+
100: {
|
|
221
|
+
game_id: 100,
|
|
222
|
+
name: "Test Slot",
|
|
223
|
+
short_name: "test-slot",
|
|
224
|
+
market: "en_za",
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const baseCtx = {
|
|
230
|
+
relations: gameRelations,
|
|
231
|
+
pages: undefined,
|
|
232
|
+
type: "casino",
|
|
233
|
+
market: "en_za",
|
|
234
|
+
filterEnabled: false,
|
|
235
|
+
filters: {},
|
|
236
|
+
data: gameRelations,
|
|
237
|
+
toplists: null,
|
|
238
|
+
toplistLabel: undefined,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
test("processes game operators through processOperatorToplist", () => {
|
|
242
|
+
const item = {
|
|
243
|
+
game_id: 100,
|
|
244
|
+
operators: [
|
|
245
|
+
{ operator_id: 6342, selling_points: ["a"], updated_at: "2025-01-14" },
|
|
246
|
+
],
|
|
247
|
+
updated_at: "2025-01-14",
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const result = processGameToplist(item, baseCtx);
|
|
251
|
+
|
|
252
|
+
expect(result).not.toBeNull();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("returns object with undefined fields when game relation not found", () => {
|
|
256
|
+
const item = {
|
|
257
|
+
game_id: 99999,
|
|
258
|
+
operators: [],
|
|
259
|
+
updated_at: "2025-01-14",
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const result = processGameToplist(item, baseCtx);
|
|
263
|
+
|
|
264
|
+
// When relation is not found, the spread of undefined still creates an object
|
|
265
|
+
// so processToplistItem receives a non-null itemRelation
|
|
266
|
+
expect(result).toBeDefined();
|
|
267
|
+
expect(result.operators).toBeUndefined();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("attaches first processed operator to the game relation", () => {
|
|
271
|
+
const item = {
|
|
272
|
+
game_id: 100,
|
|
273
|
+
operators: [
|
|
274
|
+
{ operator_id: 6342, selling_points: ["a"], updated_at: "2025-01-14" },
|
|
275
|
+
{ operator_id: 6343, selling_points: ["b"], updated_at: "2025-01-14" },
|
|
276
|
+
],
|
|
277
|
+
updated_at: "2025-01-14",
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const result = processGameToplist(item, baseCtx);
|
|
281
|
+
|
|
282
|
+
expect(result).not.toBeNull();
|
|
283
|
+
// The first processed operator should be attached as the operators field
|
|
284
|
+
if (result.operators) {
|
|
285
|
+
expect(result.operators.short_name).toBeDefined();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
package/src/helpers/tracker.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/* eslint-disable no-underscore-dangle */
|
|
3
3
|
import { generateTrackerLink } from "./generators.mjs";
|
|
4
4
|
import { stripSuffixSlash } from "./strings.mjs";
|
|
5
|
-
import { getCookie } from "./cookies.
|
|
5
|
+
import { getCookie } from "./cookies.mjs";
|
|
6
6
|
|
|
7
7
|
export function getTrackerName(operator, page, path) {
|
|
8
8
|
const trackerLinks = operator ? Object.keys(operator.links) : [];
|
package/src/resolver/modules.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/* eslint-disable import/no-extraneous-dependencies */
|
|
5
5
|
import loadash from "lodash/index.js";
|
|
6
6
|
import { generatePlaceholderString } from "../helpers/generators.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { processOperatorToplist, processGameToplist } from "../helpers/toplist.mjs";
|
|
8
8
|
import {
|
|
9
9
|
clonePageForCards,
|
|
10
10
|
groupBy,
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
getModuleTitle,
|
|
20
20
|
trailingSlash,
|
|
21
21
|
} from "../helpers/getters.mjs";
|
|
22
|
-
import buildToplistFilter from "./build-toplist-filter.mjs";
|
|
23
22
|
|
|
24
23
|
const pagesGroupedByTemplateId = [];
|
|
25
24
|
const cardItems = {};
|
|
@@ -375,100 +374,56 @@ export function processTopListModule(
|
|
|
375
374
|
payout_rate: {},
|
|
376
375
|
licenses: {},
|
|
377
376
|
};
|
|
378
|
-
|
|
379
377
|
module.items = module.items.map((listItem) => {
|
|
380
378
|
const { type } = listItem;
|
|
381
379
|
const showLoadMore = +listItem.show_load_more;
|
|
382
380
|
const itemsCount = +listItem.num_items_initial_load;
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
381
|
+
const toplistItem = toplists && listItem.id
|
|
382
|
+
? toplists[listItem.id.toString()] ||
|
|
383
|
+
toplists[`g_${listItem.id.toString()}`]
|
|
384
|
+
: undefined;
|
|
385
|
+
if (toplistItem) {
|
|
386
|
+
listItem.market = toplistItem.market?.short_code;
|
|
387
|
+
listItem.items = toplistItem.items;
|
|
388
|
+
listItem.one_liner = toplistItem.one_liner;
|
|
389
|
+
listItem.tracker = toplistItem.tracker;
|
|
388
390
|
}
|
|
389
|
-
|
|
390
391
|
if (!showLoadMore && itemsCount) {
|
|
391
392
|
listItem.items = listItem.items?.slice(0, itemsCount);
|
|
392
393
|
}
|
|
394
|
+
const ctx = {
|
|
395
|
+
relations,
|
|
396
|
+
pages,
|
|
397
|
+
type,
|
|
398
|
+
market: listItem.market,
|
|
399
|
+
filterEnabled,
|
|
400
|
+
filters,
|
|
401
|
+
data,
|
|
402
|
+
toplists,
|
|
403
|
+
toplistLabel: toplists && toplistItem?.label,
|
|
404
|
+
};
|
|
393
405
|
|
|
406
|
+
// build top list
|
|
394
407
|
listItem.items = listItem.items
|
|
395
408
|
? listItem.items
|
|
396
|
-
.
|
|
397
|
-
|
|
409
|
+
.filter(
|
|
410
|
+
(item) =>
|
|
398
411
|
Object.values(relations.operator).find(
|
|
399
412
|
(operator) =>
|
|
400
413
|
operator.operator_id === item.operator_id &&
|
|
401
414
|
operator.market === listItem.market &&
|
|
402
415
|
type === operator.type
|
|
416
|
+
) ||
|
|
417
|
+
Object.values(relations.game).find(
|
|
418
|
+
(game) => game.game_id === item.game_id
|
|
403
419
|
)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
operatorRelation.ribbons =
|
|
412
|
-
item.ribbon_ids && item.ribbon_ids.length === 0
|
|
413
|
-
? operatorRelation.ribbons
|
|
414
|
-
: item.ribbon_ids;
|
|
415
|
-
|
|
416
|
-
operatorRelation.selling_points =
|
|
417
|
-
item.selling_points && item.selling_points.length === 0
|
|
418
|
-
? operatorRelation.selling_points
|
|
419
|
-
: item.selling_points;
|
|
420
|
-
|
|
421
|
-
operatorRelation.updated_at = item.updated_at;
|
|
422
|
-
operatorRelation.toplist_bonus = item.bonus_name;
|
|
423
|
-
operatorRelation.toplist_item_tracker_name = item.tracker_name;
|
|
424
|
-
|
|
425
|
-
const operatorPage =
|
|
426
|
-
pages && pages.operator && operatorRelation
|
|
427
|
-
? pages.operator.filter(
|
|
428
|
-
(page) =>
|
|
429
|
-
page.relation &&
|
|
430
|
-
page.relation.operator_id ===
|
|
431
|
-
operatorRelation.operator_id &&
|
|
432
|
-
page.relation.type === operatorRelation.type
|
|
433
|
-
)
|
|
434
|
-
: [];
|
|
435
|
-
|
|
436
|
-
const reviewLink =
|
|
437
|
-
process.env.DMS_REVIEW_LINKS_ENABLED === "true"
|
|
438
|
-
? stripDomainAndLeadingSlash(operatorRelation?.review_link)
|
|
439
|
-
: operatorPage[0]?.path;
|
|
440
|
-
|
|
441
|
-
const authorTitle =
|
|
442
|
-
operatorPage[0] && operatorPage[0]?.author?.author_title;
|
|
443
|
-
|
|
444
|
-
const clone = sanitizeOperatorData(
|
|
445
|
-
cloneDeep(operatorRelation),
|
|
446
|
-
operatorPage,
|
|
447
|
-
data,
|
|
448
|
-
toplists && toplists[listItem.id].label
|
|
449
|
-
);
|
|
450
|
-
|
|
451
|
-
const socialAuthorLinks = [
|
|
452
|
-
"facebook_profile",
|
|
453
|
-
"twitter_profile",
|
|
454
|
-
"instagram_profile",
|
|
455
|
-
"linkedin_profile",
|
|
456
|
-
];
|
|
457
|
-
if (filterEnabled) {
|
|
458
|
-
buildToplistFilter(filters, clone);
|
|
459
|
-
}
|
|
460
|
-
return Object.assign(clone, {
|
|
461
|
-
review_link: reviewLink,
|
|
462
|
-
author_title: authorTitle,
|
|
463
|
-
author_same_as:
|
|
464
|
-
operatorPage[0] &&
|
|
465
|
-
operatorPage[0]?.author &&
|
|
466
|
-
socialAuthorLinks
|
|
467
|
-
.map((socialLink) => operatorPage[0]?.author?.[socialLink])
|
|
468
|
-
.filter((socialLink) => socialLink),
|
|
469
|
-
});
|
|
420
|
+
)
|
|
421
|
+
.map((item) => {
|
|
422
|
+
if (item.operator_id) return processOperatorToplist(item, ctx);
|
|
423
|
+
if (item.game_id) return processGameToplist(item, ctx);
|
|
424
|
+
return null;
|
|
470
425
|
})
|
|
471
|
-
.filter(
|
|
426
|
+
.filter(Boolean)
|
|
472
427
|
: [];
|
|
473
428
|
const latestItems = listItem.items
|
|
474
429
|
.map((toplist) => ({
|
|
@@ -477,7 +432,6 @@ export function processTopListModule(
|
|
|
477
432
|
}))
|
|
478
433
|
.sort((a, b) => b.updated_at - a.updated_at)
|
|
479
434
|
.slice(0, 3);
|
|
480
|
-
|
|
481
435
|
const formattedLatestItems = latestItems
|
|
482
436
|
.map((item) => item.name)
|
|
483
437
|
.reduce((acc, curr, index, array) => {
|
|
@@ -626,9 +580,13 @@ export function processAnchor(module = {}, relationData, translations) {
|
|
|
626
580
|
relationData
|
|
627
581
|
);
|
|
628
582
|
anchor.label = generatedLabel;
|
|
629
|
-
anchor.slug = generatePlaceholderString(
|
|
583
|
+
anchor.slug = generatePlaceholderString(
|
|
584
|
+
anchor?.slug?.trim(),
|
|
585
|
+
translations,
|
|
586
|
+
relationData
|
|
587
|
+
);
|
|
630
588
|
});
|
|
631
|
-
|
|
589
|
+
|
|
632
590
|
return module;
|
|
633
591
|
}
|
|
634
592
|
|
|
File without changes
|