@saooti/octopus-sdk 41.8.1 → 41.8.2

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.
@@ -9,7 +9,8 @@
9
9
  "Bash(python3:*)",
10
10
  "Bash(node --version:*)",
11
11
  "Bash(npm:*)",
12
- "Bash(npx vitest:*)"
12
+ "Bash(npx vitest:*)",
13
+ "Bash(./node_modules/.bin/vitest run:*)"
13
14
  ]
14
15
  }
15
16
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 41.8.2 (23/03/2026)
4
+
5
+ **Fix**
6
+
7
+ - **14083** Ajustements système de saison
8
+ - Le bon dernier épisode est récupéré sur les pages d'émissions
9
+ - Les saisons sont correctement énumérées sur les pages d'émissions
10
+
3
11
  ## 41.8.1 (20/03/2026)
4
12
 
5
13
  **Fix**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "41.8.1",
3
+ "version": "41.8.2",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
@@ -36,8 +36,17 @@ export const useSeasonsManagement = () => {
36
36
  }
37
37
  }
38
38
 
39
+ /**
40
+ * Returns the highest season number for the given emission, or -Infinity if none
41
+ * @param emission The emission to check
42
+ */
43
+ function getMaxSeason(emission: Emission | undefined): number {
44
+ return Math.max(...(emission?.seasons ?? []));
45
+ }
46
+
39
47
  return {
40
48
  areSeasonsEnabled,
41
- formatSeason
49
+ formatSeason,
50
+ getMaxSeason,
42
51
  }
43
52
  }
@@ -39,13 +39,13 @@
39
39
  <ClassicNav
40
40
  v-else
41
41
  v-model:active-tab="activeSeasonTab"
42
- :tab-number="seasons.length"
42
+ :tab-number="emission.seasons.length"
43
43
  >
44
- <template v-for="season in seasons" #[tabNameSlot(season)]>
44
+ <template v-for="season in emission.seasons" #[tabNameSlot(season)]>
45
45
  {{ $t('Podcast - Season N', { season }) }}
46
46
  </template>
47
47
 
48
- <template v-for="season in seasons" #[tabContentSlot(season)] :key="season">
48
+ <template v-for="season in emission.seasons" #[tabContentSlot(season)] :key="season">
49
49
  <PodcastList
50
50
  class="flex-grow-1"
51
51
  :first="dfirst"
@@ -123,7 +123,7 @@ const iabId : Ref<number | undefined>= ref(undefined);
123
123
 
124
124
  //Composables
125
125
  const { t } = useI18n();
126
- const { areSeasonsEnabled } = useSeasonsManagement();
126
+ const { areSeasonsEnabled, getMaxSeason } = useSeasonsManagement();
127
127
 
128
128
  //Computed
129
129
  const titleFilter = computed(() => {
@@ -134,10 +134,10 @@ const titleFilter = computed(() => {
134
134
  const query = computed(() => searchPattern.value.length > 3 ? searchPattern.value : "");
135
135
 
136
136
  const showSeasons = computed(() => {
137
- return props.emission !== undefined && areSeasonsEnabled(props.emission) && props.emission.seasonCount > 0;
137
+ return props.emission !== undefined && areSeasonsEnabled(props.emission) && (props.emission.seasons?.length ?? 0) > 0;
138
138
  });
139
139
 
140
- const activeSeasonTab = ref(showSeasons.value ? (props.emission?.seasonCount ?? 1) - 1 : 0);
140
+ const activeSeasonTab = ref(showSeasons.value ? (getMaxSeason(props.emission) - 1) : 0);
141
141
 
142
142
  const sort = computed((): PodcastSort => {
143
143
  if(showSeasons.value === true) {
@@ -149,16 +149,6 @@ const sort = computed((): PodcastSort => {
149
149
  }
150
150
  });
151
151
 
152
- const seasons = computed((): Array<number> => {
153
- const ary: Array<number> = [];
154
- if (showSeasons.value === true) {
155
- for (let i = 1; i <= props.emission.seasonCount; i++) {
156
- ary.push(i);
157
- }
158
- }
159
- return ary;
160
- });
161
-
162
152
  //Watch
163
153
  watch(()=>props.reload, () => {
164
154
  reloadList.value = !reloadList.value;
@@ -204,7 +204,7 @@ const {
204
204
  paginateFirst,
205
205
  isInit
206
206
  } = useSimplePageParam(props, true);
207
- const { areSeasonsEnabled, formatSeason } = useSeasonsManagement();
207
+ const { areSeasonsEnabled, formatSeason, getMaxSeason } = useSeasonsManagement();
208
208
 
209
209
 
210
210
  //Computed
@@ -259,16 +259,22 @@ async function getEmissionDetails(): Promise<void> {
259
259
  initError();
260
260
  }
261
261
  }
262
+
262
263
  function podcastsFetched(podcasts: Array<Podcast>, season: number|undefined) {
263
- if (season !== undefined && season !== emission.value?.seasonCount) {
264
- return;
265
- }
264
+ const isReadyAndVisible = (p: Podcast) => "READY" === p.processingStatus && p.availability.visibility;
266
265
 
267
- for (const podcast of podcasts) {
268
- if ("READY" === podcast.processingStatus && podcast.availability.visibility) {
269
- lastPodcast.value = podcast;
266
+ if (areSeasonsEnabled(emission.value)) {
267
+ // Ignore results that are not from the last season
268
+ const maxSeason = getMaxSeason(emission.value);
269
+ if (season !== undefined && season !== maxSeason) {
270
270
  return;
271
271
  }
272
+ // If seasons are enabled, take last element
273
+ lastPodcast.value = podcasts.findLast(isReadyAndVisible);
274
+ } else {
275
+ // If seasons are disabled, we have a standard date desc sort, so we take
276
+ // first element
277
+ lastPodcast.value = podcasts.find(isReadyAndVisible);
272
278
  }
273
279
  }
274
280
 
@@ -54,8 +54,8 @@ export interface Emission {
54
54
  groupIds?: Array<number>
55
55
  /** Seasons configuration */
56
56
  seasonMode: SeasonMode;
57
- /** Number of seasons on this emission */
58
- seasonCount: number;
57
+ /** Seasons defined on emission */
58
+ seasons?: Array<number>;
59
59
  /** Indicates that the emission has explicit content */
60
60
  explicit?: boolean;
61
61
  }
@@ -72,7 +72,6 @@ export function emptyEmissionData(orga?: Organisation): Emission {
72
72
  rubriqueIds: [],
73
73
  monetisable: "UNDEFINED",
74
74
  limits: {},
75
- seasonMode: SeasonMode.NO_SEASON,
76
- seasonCount: 0
75
+ seasonMode: SeasonMode.NO_SEASON
77
76
  };
78
77
  }
@@ -23,7 +23,7 @@ import { emissionApi } from '@/api/emissionApi';
23
23
  const publicOrga = { id: 'org-1', name: 'Test', imageUrl: '', privacy: 'PUBLIC' };
24
24
 
25
25
  function makeEmission(seasonMode: SeasonMode) {
26
- return { ...emptyEmissionData(), seasonMode, seasonCount: 2, orga: publicOrga };
26
+ return { ...emptyEmissionData(), seasonMode, seasons: [1, 2], orga: publicOrga };
27
27
  }
28
28
 
29
29
  function makeReadyPodcast(seasonMode: SeasonMode = SeasonMode.NO_SEASON) {
@@ -40,8 +40,18 @@ async function mountPage(seasonMode: SeasonMode) {
40
40
  return mount(EmissionPage, { shallow: true, props: { emissionId: 1 } });
41
41
  }
42
42
 
43
- async function triggerFetch(wrapper: VueWrapper, seasonMode: SeasonMode, season?: number) {
44
- await wrapper.findComponent({ name: 'PodcastFilterList' }).vm.$emit('fetch', [makeReadyPodcast(seasonMode)], season);
43
+ async function mountWithSeasons(seasons: number[]) {
44
+ vi.mocked(emissionApi.get).mockResolvedValue({
45
+ ...emptyEmissionData(),
46
+ seasonMode: SeasonMode.SEASON_WITH_PODCAST_NUMBERING,
47
+ seasons,
48
+ orga: publicOrga,
49
+ });
50
+ return mount(EmissionPage, { shallow: true, props: { emissionId: 1 } });
51
+ }
52
+
53
+ async function triggerFetch(wrapper: VueWrapper, seasonMode: SeasonMode, season?: number, podcasts?: ReturnType<typeof makeReadyPodcast>[]) {
54
+ await wrapper.findComponent({ name: 'PodcastFilterList' }).vm.$emit('fetch', podcasts ?? [makeReadyPodcast(seasonMode)], season);
45
55
  await nextTick();
46
56
  }
47
57
 
@@ -70,7 +80,7 @@ describe('EmissionPage', () => {
70
80
  });
71
81
 
72
82
  describe('podcastsFetched season filtering', () => {
73
- // seasonCount is 2 in makeEmission
83
+ // seasons is [1, 2] in makeEmission, so max season is 2
74
84
  it.each([undefined, 2])('shows lastPodcast when season is %s', async (season) => {
75
85
  const wrapper = await mountPage(SeasonMode.SEASON_WITH_PODCAST_NUMBERING);
76
86
  await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, season);
@@ -82,5 +92,49 @@ describe('EmissionPage', () => {
82
92
  await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, 1);
83
93
  expect(wrapper.text()).not.toContain('Listen to the latest episode');
84
94
  });
95
+
96
+ it('accepts the highest season in the seasons array', async () => {
97
+ const wrapper = await mountWithSeasons([1, 2, 3]);
98
+ await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, 3);
99
+ expect(wrapper.text()).toContain('Listen to the latest episode');
100
+ });
101
+
102
+ it('ignores a season below the max in the seasons array', async () => {
103
+ const wrapper = await mountWithSeasons([1, 2, 3]);
104
+ await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, 2);
105
+ expect(wrapper.text()).not.toContain('Listen to the latest episode');
106
+ });
107
+ });
108
+
109
+ describe('podcastsFetched podcast selection', () => {
110
+ function makePodcast(seasonMode: SeasonMode, episodeNumber: number, { ready = true, visible = true } = {}) {
111
+ const podcast = makeReadyPodcast(seasonMode);
112
+ podcast.seasonEpisodeNumber = episodeNumber;
113
+ if (!ready) podcast.processingStatus = PodcastProcessingStatus.Processing;
114
+ if (!visible) podcast.availability.visibility = false;
115
+ return podcast;
116
+ }
117
+
118
+ it('with seasons enabled, selects the last ready visible podcast', async () => {
119
+ const wrapper = await mountPage(SeasonMode.SEASON_WITH_PODCAST_NUMBERING);
120
+ const podcasts = [
121
+ makePodcast(SeasonMode.SEASON_WITH_PODCAST_NUMBERING, 1),
122
+ makePodcast(SeasonMode.SEASON_WITH_PODCAST_NUMBERING, 5),
123
+ ];
124
+ await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, undefined, podcasts);
125
+ expect(wrapper.text()).toContain('S1·E5');
126
+ });
127
+
128
+ it.each([SeasonMode.SEASON_WITH_PODCAST_NUMBERING, SeasonMode.NO_SEASON])('skips non-ready podcasts (%s)', async (seasonMode) => {
129
+ const wrapper = await mountPage(seasonMode);
130
+ await triggerFetch(wrapper, seasonMode, undefined, [makePodcast(seasonMode, 1, { ready: false })]);
131
+ expect(wrapper.text()).not.toContain('Listen to the latest episode');
132
+ });
133
+
134
+ it.each([SeasonMode.SEASON_WITH_PODCAST_NUMBERING, SeasonMode.NO_SEASON])('skips non-visible podcasts (%s)', async (seasonMode) => {
135
+ const wrapper = await mountPage(seasonMode);
136
+ await triggerFetch(wrapper, seasonMode, undefined, [makePodcast(seasonMode, 1, { visible: false })]);
137
+ expect(wrapper.text()).not.toContain('Listen to the latest episode');
138
+ });
85
139
  });
86
140
  });