@saooti/octopus-sdk 41.7.2 → 41.8.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.
Files changed (36) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CHANGELOG.md +28 -0
  3. package/index.ts +6 -2
  4. package/package.json +1 -1
  5. package/src/api/emissionApi.ts +44 -1
  6. package/src/api/podcastApi.ts +27 -5
  7. package/src/components/composable/route/useAdvancedParamInit.ts +4 -3
  8. package/src/components/composable/useRights.ts +196 -0
  9. package/src/components/composable/useSeasonsManagement.ts +43 -0
  10. package/src/components/display/live/RadioPlanning.vue +6 -4
  11. package/src/components/display/podcasts/PodcastFilterList.vue +100 -18
  12. package/src/components/display/podcasts/PodcastInlineListTemplate.vue +4 -1
  13. package/src/components/display/podcasts/PodcastList.vue +5 -1
  14. package/src/components/display/podcasts/PodcastModuleBox.vue +21 -4
  15. package/src/components/display/podcasts/PodcastSwiperList.vue +9 -3
  16. package/src/components/form/ClassicRadio.vue +13 -14
  17. package/src/components/misc/TopBar.vue +7 -1
  18. package/src/components/pages/EmissionPage.vue +22 -10
  19. package/src/locale/de.json +7 -1
  20. package/src/locale/en.json +7 -1
  21. package/src/locale/es.json +7 -1
  22. package/src/locale/fr.json +7 -1
  23. package/src/locale/it.json +7 -1
  24. package/src/locale/sl.json +7 -1
  25. package/src/stores/class/general/emission.ts +20 -0
  26. package/src/stores/class/general/podcast.ts +17 -0
  27. package/src/style/general.scss +7 -0
  28. package/tests/api/podcastApi.spec.ts +43 -0
  29. package/tests/components/composable/useAdvancedParamInit.spec.ts +90 -0
  30. package/tests/components/composable/useRights.spec.ts +265 -0
  31. package/tests/components/composable/useSeasonsManagement.spec.ts +35 -0
  32. package/tests/components/display/podcasts/PodcastFilterList.spec.ts +33 -0
  33. package/tests/components/display/podcasts/PodcastInlineListTemplate.spec.ts +23 -0
  34. package/tests/components/display/podcasts/PodcastModuleBox.spec.ts +49 -22
  35. package/tests/components/pages/EmissionPage.spec.ts +86 -0
  36. package/tests/utils.ts +12 -1
@@ -0,0 +1,86 @@
1
+ import '@tests/mocks/i18n';
2
+ import '@tests/mocks/useRouter';
3
+
4
+ import EmissionPage from '@/components/pages/EmissionPage.vue';
5
+ import { emptyEmissionData, SeasonMode } from '@/stores/class/general/emission';
6
+ import { emptyPodcastData, PodcastProcessingStatus } from '@/stores/class/general/podcast';
7
+ import { mount, VueWrapper } from '@tests/utils';
8
+ import { describe, expect, it, vi } from 'vitest';
9
+ import { nextTick } from 'vue';
10
+
11
+ vi.mock('@/api/emissionApi', () => ({
12
+ emissionApi: { get: vi.fn() }
13
+ }));
14
+ vi.mock('@/components/composable/route/useSeoTitleUrl.ts', () => ({
15
+ useSeoTitleUrl: () => ({ updatePathParams: vi.fn() })
16
+ }));
17
+ vi.mock('@/components/composable/useImageProxy', () => ({
18
+ useImageProxy: () => ({ useProxyImageUrl: vi.fn() })
19
+ }));
20
+
21
+ import { emissionApi } from '@/api/emissionApi';
22
+
23
+ const publicOrga = { id: 'org-1', name: 'Test', imageUrl: '', privacy: 'PUBLIC' };
24
+
25
+ function makeEmission(seasonMode: SeasonMode) {
26
+ return { ...emptyEmissionData(), seasonMode, seasonCount: 2, orga: publicOrga };
27
+ }
28
+
29
+ function makeReadyPodcast(seasonMode: SeasonMode = SeasonMode.NO_SEASON) {
30
+ const podcast = emptyPodcastData();
31
+ podcast.processingStatus = PodcastProcessingStatus.Ready;
32
+ podcast.seasonNumber = 1;
33
+ podcast.seasonEpisodeNumber = 3;
34
+ podcast.emission.seasonMode = seasonMode;
35
+ return podcast;
36
+ }
37
+
38
+ async function mountPage(seasonMode: SeasonMode) {
39
+ vi.mocked(emissionApi.get).mockResolvedValue(makeEmission(seasonMode));
40
+ return mount(EmissionPage, { shallow: true, props: { emissionId: 1 } });
41
+ }
42
+
43
+ async function triggerFetch(wrapper: VueWrapper, seasonMode: SeasonMode, season?: number) {
44
+ await wrapper.findComponent({ name: 'PodcastFilterList' }).vm.$emit('fetch', [makeReadyPodcast(seasonMode)], season);
45
+ await nextTick();
46
+ }
47
+
48
+ describe('EmissionPage', () => {
49
+ describe('messageListenEpisode', () => {
50
+ it('does not show season info before any podcast is fetched', async () => {
51
+ const wrapper = await mountPage(SeasonMode.SEASON_WITH_PODCAST_NUMBERING);
52
+ expect(wrapper.text()).not.toContain('S1·E3');
53
+ });
54
+
55
+ it('shows base message without season info for NO_SEASON emission', async () => {
56
+ const wrapper = await mountPage(SeasonMode.NO_SEASON);
57
+ await triggerFetch(wrapper, SeasonMode.NO_SEASON);
58
+ expect(wrapper.text()).toContain('Listen to the latest episode');
59
+ expect(wrapper.text()).not.toContain('(S');
60
+ });
61
+
62
+ it.each([
63
+ { seasonMode: SeasonMode.SEASON_WITH_PODCAST_NUMBERING, expected: 'Listen to the latest episode (S1·E3)' },
64
+ { seasonMode: SeasonMode.SEASON_WITHOUT_PODCAST_NUMBERING, expected: 'Listen to the latest episode (S1)' },
65
+ ])('appends season info for $seasonMode', async ({ seasonMode, expected }) => {
66
+ const wrapper = await mountPage(seasonMode);
67
+ await triggerFetch(wrapper, seasonMode);
68
+ expect(wrapper.text()).toContain(expected);
69
+ });
70
+ });
71
+
72
+ describe('podcastsFetched season filtering', () => {
73
+ // seasonCount is 2 in makeEmission
74
+ it.each([undefined, 2])('shows lastPodcast when season is %s', async (season) => {
75
+ const wrapper = await mountPage(SeasonMode.SEASON_WITH_PODCAST_NUMBERING);
76
+ await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, season);
77
+ expect(wrapper.text()).toContain('Listen to the latest episode');
78
+ });
79
+
80
+ it('ignores podcasts from earlier seasons', async () => {
81
+ const wrapper = await mountPage(SeasonMode.SEASON_WITH_PODCAST_NUMBERING);
82
+ await triggerFetch(wrapper, SeasonMode.SEASON_WITH_PODCAST_NUMBERING, 1);
83
+ expect(wrapper.text()).not.toContain('Listen to the latest episode');
84
+ });
85
+ });
86
+ });
package/tests/utils.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Component } from 'vue';
2
2
  import { vi } from 'vitest';
3
3
  import { mount as _mount, VueWrapper } from '@vue/test-utils';
4
- import { type Pinia, setActivePinia } from 'pinia';
4
+ import { type Pinia, setActivePinia, createPinia } from 'pinia';
5
5
  import { createTestingPinia } from '@pinia/testing';
6
6
 
7
7
  import { useAuthStore } from '../src/stores/AuthStore';
@@ -159,3 +159,14 @@ export function combineStoreSetups(...setups: Array<() => void>) {
159
159
  }
160
160
 
161
161
  export { VueWrapper };
162
+
163
+ /**
164
+ * Creates a Pinia instance without mounting a component.
165
+ * Use this for testing composables or APIs directly.
166
+ */
167
+ export function setupPinia(setupFn?: () => void | Promise<void>): Pinia {
168
+ const pinia = createPinia();
169
+ setActivePinia(pinia);
170
+ if (setupFn) { setupFn(); }
171
+ return pinia;
172
+ }