itube-specs 0.0.778 → 0.0.780
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/components/category/letter-groups.vue +1 -1
- package/components/filter/main.vue +4 -4
- package/components/filter/model-field.vue +1 -1
- package/components/filter/model.vue +1 -1
- package/components/filter/page.vue +2 -2
- package/components/filter/slider.vue +2 -2
- package/components/filter/videos-chips.vue +1 -1
- package/components/info/main.vue +1 -1
- package/components/layout/header.vue +1 -1
- package/components/layout/language.vue +1 -1
- package/components/player/channel.vue +1 -1
- package/components/player/like.vue +2 -2
- package/components/search/result.vue +1 -1
- package/components/section/seo-text.vue +1 -1
- package/components/ui/full-descr.vue +1 -1
- package/components/ui/pagination.vue +2 -2
- package/package.json +1 -1
- package/components/auth/__tests__/login.test.ts +0 -123
- package/components/auth/__tests__/recovery.test.ts +0 -86
- package/components/auth/__tests__/register.test.ts +0 -122
- package/components/player/__tests__/like.test.ts +0 -85
- package/components/playlist/__tests__/add.test.ts +0 -115
- package/components/playlist/__tests__/edit.test.ts +0 -91
- package/components/profile/__tests__/password.test.ts +0 -60
- package/components/profile/__tests__/setup.test.ts +0 -100
- package/components/report/__tests__/main.test.ts +0 -106
- package/composables/__tests__/use-next-video.test.ts +0 -123
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
</template>
|
|
12
12
|
|
|
13
13
|
<script setup lang="ts" generic="T extends Record<string, any>">
|
|
14
|
-
import { groupByFirstLetter } from '
|
|
14
|
+
import { groupByFirstLetter } from '../../utils/category-letter-groups/group-by-first-letter';
|
|
15
15
|
|
|
16
16
|
const props = defineProps<{
|
|
17
17
|
items: T[];
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
<script setup lang="ts">
|
|
45
45
|
import type { ButtonSizes, ButtonThemes, IChipsItem, IFilterScheme, ISelectItem } from '../../types';
|
|
46
46
|
import type { LocationQuery } from '#vue-router';
|
|
47
|
-
import { parseFilterCategories } from '
|
|
48
|
-
import { parseCategoryFilterValues } from '
|
|
49
|
-
import { getActiveFilterValues } from '
|
|
50
|
-
import { buildFilterQuery } from '
|
|
47
|
+
import { parseFilterCategories } from '../../utils/parse-filter-categories';
|
|
48
|
+
import { parseCategoryFilterValues } from '../../utils/filter-main/parse-category-filter-values';
|
|
49
|
+
import { getActiveFilterValues } from '../../utils/filter-main/get-active-filter-values';
|
|
50
|
+
import { buildFilterQuery } from '../../utils/filter-main/build-filter-query';
|
|
51
51
|
|
|
52
52
|
const props = defineProps<{
|
|
53
53
|
scheme: IFilterScheme[]
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<script setup lang="ts">
|
|
51
51
|
import type { IModelFilter } from '../../types';
|
|
52
52
|
import type { LocationQuery } from '#vue-router';
|
|
53
|
-
import { sortFilterRadioOptions } from '
|
|
53
|
+
import { sortFilterRadioOptions } from '../../utils/sort-filter-radio-options';
|
|
54
54
|
|
|
55
55
|
const HEIGHT_FILTER_NAMES = ['height_cm', 'height_in'];
|
|
56
56
|
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
<script setup lang="ts">
|
|
53
53
|
import type { IChipsItem, IModelCard, IModelFilter, PaginatedResponse } from '../../types';
|
|
54
54
|
import type { LocationQuery } from '#vue-router';
|
|
55
|
-
import { getSortedFilterGroups } from '
|
|
55
|
+
import { getSortedFilterGroups } from '../../utils/get-sorted-filter-groups';
|
|
56
56
|
|
|
57
57
|
const props = defineProps<{
|
|
58
58
|
data: PaginatedResponse<IModelCard>,
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
import type { IModelFilter } from '../../types';
|
|
74
74
|
|
|
75
75
|
import { useRoute, useRouter } from 'vue-router';
|
|
76
|
-
import { sortFilterRadioOptions } from '
|
|
77
|
-
import { getSortedFilterGroups } from '
|
|
76
|
+
import { sortFilterRadioOptions } from '../../utils/sort-filter-radio-options';
|
|
77
|
+
import { getSortedFilterGroups } from '../../utils/get-sorted-filter-groups';
|
|
78
78
|
|
|
79
79
|
const props = defineProps<{
|
|
80
80
|
filters: IModelFilter[]
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
import type { IModelFilter, IModelFilterOptions } from '../../types';
|
|
34
34
|
import type { LocationQuery } from '#vue-router';
|
|
35
35
|
import { useRoute, useRouter } from '#vue-router';
|
|
36
|
-
import { getSliderRangeInfo } from '
|
|
37
|
-
import { buildRangeFilterQuery } from '
|
|
36
|
+
import { getSliderRangeInfo } from '../../utils/filter-slider/get-slider-range-info';
|
|
37
|
+
import { buildRangeFilterQuery } from '../../utils/filter-slider/build-range-filter-query';
|
|
38
38
|
|
|
39
39
|
const props = defineProps<{
|
|
40
40
|
item: IModelFilter
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
<script setup lang="ts">
|
|
11
11
|
import type { IChipsItem, ISelectItem, IFilterScheme } from '../../types';
|
|
12
|
-
import { parseFilterCategories } from '
|
|
12
|
+
import { parseFilterCategories } from '../../utils/parse-filter-categories';
|
|
13
13
|
|
|
14
14
|
const route = useRoute();
|
|
15
15
|
const router = useRouter();
|
package/components/info/main.vue
CHANGED
|
@@ -136,7 +136,7 @@ import type {
|
|
|
136
136
|
ICategoryCard
|
|
137
137
|
} from '../../types';
|
|
138
138
|
import { AuthorizationApiService } from '../../services/api/authorization.service';
|
|
139
|
-
import { groupParametersByGroup } from '
|
|
139
|
+
import { groupParametersByGroup } from '../../utils/info-main/group-parameters-by-group';
|
|
140
140
|
|
|
141
141
|
const { t } = useI18n();
|
|
142
142
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
import { AuthSteps } from '../../runtime';
|
|
79
79
|
import { onClickOutside } from '@vueuse/core';
|
|
80
80
|
import { AuthorizationApiService } from '../../services/api/authorization.service';
|
|
81
|
-
import { isoCodeMap } from '
|
|
81
|
+
import { isoCodeMap } from '../../utils/iso-code-map';
|
|
82
82
|
|
|
83
83
|
const clientCountryCode = useState<string>('clientCountryCode', () => '');
|
|
84
84
|
const clientCountryIcon = computed(() => {
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
|
|
97
97
|
<script setup lang="ts">
|
|
98
98
|
import type { ISelectItem } from '../../types';
|
|
99
|
-
import { isoCodeMap } from '
|
|
99
|
+
import { isoCodeMap } from '../../utils/iso-code-map';
|
|
100
100
|
import { DictionariesApiService } from '../../services/api/dictionaries.service';
|
|
101
101
|
|
|
102
102
|
const { locale, locales } = useI18n();
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
<script setup lang="ts">
|
|
36
36
|
import { convertString, AuthSteps } from '../../runtime';
|
|
37
37
|
import { AuthorizationApiService } from '../../services/api/authorization.service';
|
|
38
|
-
import { getNameInitials } from '
|
|
38
|
+
import { getNameInitials } from '../../utils/player-channel/get-name-initials';
|
|
39
39
|
|
|
40
40
|
const props = defineProps<{
|
|
41
41
|
name: string
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
<script setup lang="ts">
|
|
48
48
|
import { VideosApiService } from '../../services/api/videos.service';
|
|
49
49
|
import { PlaylistsApiService } from '../../services/api/playlists.service';
|
|
50
|
-
import { applyReaction } from '
|
|
51
|
-
import { getReactionPercents } from '
|
|
50
|
+
import { applyReaction } from '../../utils/player-like/apply-reaction';
|
|
51
|
+
import { getReactionPercents } from '../../utils/player-like/get-reaction-percents';
|
|
52
52
|
|
|
53
53
|
const { t } = useI18n();
|
|
54
54
|
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
<script setup lang="ts">
|
|
106
106
|
import type { IModelCard } from '../../types';
|
|
107
107
|
import { AsyncData } from '../../runtime';
|
|
108
|
-
import { buildSearchTabs } from '
|
|
108
|
+
import { buildSearchTabs } from '../../utils/search-result/build-search-tabs';
|
|
109
109
|
import { SearchApiService } from '../../services/api/search.service';
|
|
110
110
|
import { ModelsApiService } from '../../services/api/models.service';
|
|
111
111
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
</template>
|
|
23
23
|
|
|
24
24
|
<script setup lang="ts">
|
|
25
|
-
import { splitTextByKeywordLinks } from '
|
|
25
|
+
import { splitTextByKeywordLinks } from '../../utils/section-seo-text/split-text-by-keyword-links';
|
|
26
26
|
|
|
27
27
|
const props = defineProps<{
|
|
28
28
|
text?: string
|
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
<script setup lang="ts">
|
|
116
116
|
// не забыват проставлять key на компоненте для переинициализации и отслеживания props.total чтобы пересчитывалось правильное сео
|
|
117
117
|
import type { ButtonThemes } from '../../types';
|
|
118
|
-
import { getPaginationNumbers } from '
|
|
119
|
-
import { getPaginationLink } from '
|
|
118
|
+
import { getPaginationNumbers } from '../../utils/ui-pagination/get-pagination-numbers';
|
|
119
|
+
import { getPaginationLink } from '../../utils/ui-pagination/get-pagination-link';
|
|
120
120
|
|
|
121
121
|
const props = withDefaults(defineProps<{
|
|
122
122
|
prefix?: string
|
package/package.json
CHANGED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import AuthLogin from '../login.vue';
|
|
6
|
-
|
|
7
|
-
const h = vi.hoisted(() => {
|
|
8
|
-
const login = vi.fn();
|
|
9
|
-
const closeAuthPopup = vi.fn();
|
|
10
|
-
const showError = vi.fn();
|
|
11
|
-
const showSuccess = vi.fn();
|
|
12
|
-
const setErrorState = vi.fn();
|
|
13
|
-
const resetSnackbar = vi.fn();
|
|
14
|
-
return {
|
|
15
|
-
login,
|
|
16
|
-
closeAuthPopup,
|
|
17
|
-
showError,
|
|
18
|
-
showSuccess,
|
|
19
|
-
setErrorState,
|
|
20
|
-
resetSnackbar,
|
|
21
|
-
useUserMock: vi.fn(() => ({ login })),
|
|
22
|
-
useAuthPopupMock: vi.fn(() => ({ closeAuthPopup })),
|
|
23
|
-
useSnackbarMock: vi.fn(() => ({ showError, showSuccess, setErrorState, resetSnackbar })),
|
|
24
|
-
};
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
mockNuxtImport('useUser', () => h.useUserMock);
|
|
28
|
-
mockNuxtImport('useAuthPopup', () => h.useAuthPopupMock);
|
|
29
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
30
|
-
|
|
31
|
-
const VALID_USER = 'john_doe';
|
|
32
|
-
const VALID_PASS = 'secret';
|
|
33
|
-
|
|
34
|
-
function mountLogin() {
|
|
35
|
-
return mountSuspended(AuthLogin, {
|
|
36
|
-
global: { stubs: { AuthIcon: true, UiIcon: true } },
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function setCredentials(wrapper: Awaited<ReturnType<typeof mountLogin>>, user: string, pass: string) {
|
|
41
|
-
const inputs = wrapper.findAll('input');
|
|
42
|
-
await inputs[0].setValue(user);
|
|
43
|
-
await inputs[1].setValue(pass);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
beforeEach(() => {
|
|
47
|
-
vi.clearAllMocks();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('AuthLogin', () => {
|
|
51
|
-
it('эмитит forgot по клику «забыли пароль»', async () => {
|
|
52
|
-
const wrapper = await mountLogin();
|
|
53
|
-
await wrapper.find('.auth-login__forgot').trigger('click');
|
|
54
|
-
expect(wrapper.emitted('forgot')).toBeTruthy();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('эмитит create по клику «регистрация»', async () => {
|
|
58
|
-
const wrapper = await mountLogin();
|
|
59
|
-
await wrapper.find('.auth-login__back-button').trigger('click');
|
|
60
|
-
expect(wrapper.emitted('create')).toBeTruthy();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('невалидный username → поле в ошибке, снекбар error_username, login не вызывается', async () => {
|
|
64
|
-
const wrapper = await mountLogin();
|
|
65
|
-
await setCredentials(wrapper, 'a', VALID_PASS);
|
|
66
|
-
await wrapper.find('.auth-login__login').trigger('click');
|
|
67
|
-
await flushPromises();
|
|
68
|
-
expect(wrapper.findAll('.ui-input')[0].classes()).toContain('ui-input--error');
|
|
69
|
-
expect(h.showError).toHaveBeenCalledWith('error_username');
|
|
70
|
-
expect(h.login).not.toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('невалидный password → поле в ошибке, снекбар error_password, login не вызывается', async () => {
|
|
74
|
-
const wrapper = await mountLogin();
|
|
75
|
-
await setCredentials(wrapper, VALID_USER, 'abc');
|
|
76
|
-
await wrapper.find('.auth-login__login').trigger('click');
|
|
77
|
-
await flushPromises();
|
|
78
|
-
expect(wrapper.findAll('.ui-input')[1].classes()).toContain('ui-input--error');
|
|
79
|
-
expect(h.showError).toHaveBeenCalledWith('error_password');
|
|
80
|
-
expect(h.login).not.toHaveBeenCalled();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('login очищает снекбар в начале (resetSnackbar)', async () => {
|
|
84
|
-
const wrapper = await mountLogin();
|
|
85
|
-
await setCredentials(wrapper, 'a', 'abc');
|
|
86
|
-
await wrapper.find('.auth-login__login').trigger('click');
|
|
87
|
-
await flushPromises();
|
|
88
|
-
expect(h.resetSnackbar).toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('валидные данные → login(form), снекбар успеха, закрытие попапа', async () => {
|
|
92
|
-
h.login.mockResolvedValueOnce(undefined);
|
|
93
|
-
const wrapper = await mountLogin();
|
|
94
|
-
await setCredentials(wrapper, VALID_USER, VALID_PASS);
|
|
95
|
-
await wrapper.find('.auth-login__login').trigger('click');
|
|
96
|
-
await flushPromises();
|
|
97
|
-
expect(h.login).toHaveBeenCalledWith({ username: VALID_USER, password_hash: VALID_PASS });
|
|
98
|
-
expect(h.closeAuthPopup).toHaveBeenCalled();
|
|
99
|
-
expect(h.showSuccess).toHaveBeenCalledWith('Welcome back!');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('ошибка login → снекбар ошибки (setErrorState), попап не закрывается', async () => {
|
|
103
|
-
const err = new Error('bad creds');
|
|
104
|
-
h.login.mockRejectedValueOnce(err);
|
|
105
|
-
const wrapper = await mountLogin();
|
|
106
|
-
await setCredentials(wrapper, VALID_USER, VALID_PASS);
|
|
107
|
-
await wrapper.find('.auth-login__login').trigger('click');
|
|
108
|
-
await flushPromises();
|
|
109
|
-
expect(h.login).toHaveBeenCalled();
|
|
110
|
-
expect(h.setErrorState).toHaveBeenCalledWith(err);
|
|
111
|
-
expect(h.closeAuthPopup).not.toHaveBeenCalled();
|
|
112
|
-
expect(h.showSuccess).not.toHaveBeenCalled();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('Enter в поле пароля запускает вход', async () => {
|
|
116
|
-
h.login.mockResolvedValueOnce(undefined);
|
|
117
|
-
const wrapper = await mountLogin();
|
|
118
|
-
await setCredentials(wrapper, VALID_USER, VALID_PASS);
|
|
119
|
-
await wrapper.find('input[type="password"]').trigger('keyup.enter');
|
|
120
|
-
await flushPromises();
|
|
121
|
-
expect(h.login).toHaveBeenCalled();
|
|
122
|
-
});
|
|
123
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import AuthRecovery from '../recovery.vue';
|
|
6
|
-
|
|
7
|
-
const h = vi.hoisted(() => {
|
|
8
|
-
const password = vi.fn();
|
|
9
|
-
const closeAuthPopup = vi.fn();
|
|
10
|
-
const showError = vi.fn();
|
|
11
|
-
const showSuccess = vi.fn();
|
|
12
|
-
const setErrorState = vi.fn();
|
|
13
|
-
const resetSnackbar = vi.fn();
|
|
14
|
-
const getRecaptchaToken = vi.fn(() => Promise.resolve('test-token'));
|
|
15
|
-
const initRecaptcha = vi.fn(() => Promise.resolve());
|
|
16
|
-
return {
|
|
17
|
-
password,
|
|
18
|
-
closeAuthPopup,
|
|
19
|
-
showError,
|
|
20
|
-
showSuccess,
|
|
21
|
-
setErrorState,
|
|
22
|
-
resetSnackbar,
|
|
23
|
-
getRecaptchaToken,
|
|
24
|
-
initRecaptcha,
|
|
25
|
-
useUserMock: vi.fn(() => ({ password })),
|
|
26
|
-
useAuthPopupMock: vi.fn(() => ({ closeAuthPopup })),
|
|
27
|
-
useSnackbarMock: vi.fn(() => ({ showError, showSuccess, setErrorState, resetSnackbar })),
|
|
28
|
-
useRecaptchaMock: vi.fn(() => ({ getRecaptchaToken, initRecaptcha })),
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
mockNuxtImport('useUser', () => h.useUserMock);
|
|
33
|
-
mockNuxtImport('useAuthPopup', () => h.useAuthPopupMock);
|
|
34
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
35
|
-
mockNuxtImport('useRecaptcha', () => h.useRecaptchaMock);
|
|
36
|
-
|
|
37
|
-
function mountRecovery() {
|
|
38
|
-
return mountSuspended(AuthRecovery, {
|
|
39
|
-
global: { stubs: { AuthIcon: true, UiIcon: true } },
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
vi.clearAllMocks();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('AuthRecovery', () => {
|
|
48
|
-
it('эмитит login по клику «назад ко входу»', async () => {
|
|
49
|
-
const wrapper = await mountRecovery();
|
|
50
|
-
await wrapper.find('.auth-recovery__login').trigger('click');
|
|
51
|
-
expect(wrapper.emitted('login')).toBeTruthy();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('невалидный email → поле в ошибке, снекбар error_email, password не вызывается', async () => {
|
|
55
|
-
const wrapper = await mountRecovery();
|
|
56
|
-
await wrapper.find('input').setValue('bad');
|
|
57
|
-
await wrapper.find('.auth-recovery__button').trigger('click');
|
|
58
|
-
await flushPromises();
|
|
59
|
-
expect(wrapper.find('.ui-input').classes()).toContain('ui-input--error');
|
|
60
|
-
expect(h.showError).toHaveBeenCalledWith('error_email');
|
|
61
|
-
expect(h.password).not.toHaveBeenCalled();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('валидный email → password(form с токеном), закрытие попапа, снекбар успеха', async () => {
|
|
65
|
-
h.password.mockResolvedValueOnce(undefined);
|
|
66
|
-
const wrapper = await mountRecovery();
|
|
67
|
-
await wrapper.find('input').setValue('user@example.com');
|
|
68
|
-
await wrapper.find('.auth-recovery__button').trigger('click');
|
|
69
|
-
await flushPromises();
|
|
70
|
-
expect(h.getRecaptchaToken).toHaveBeenCalledWith('password_reset');
|
|
71
|
-
expect(h.password).toHaveBeenCalledWith({ email: 'user@example.com', token: 'test-token' });
|
|
72
|
-
expect(h.closeAuthPopup).toHaveBeenCalled();
|
|
73
|
-
expect(h.showSuccess).toHaveBeenCalledWith('Check your email');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('ошибка запроса → снекбар ошибки, попап не закрывается', async () => {
|
|
77
|
-
const err = new Error('not found');
|
|
78
|
-
h.password.mockRejectedValueOnce(err);
|
|
79
|
-
const wrapper = await mountRecovery();
|
|
80
|
-
await wrapper.find('input').setValue('user@example.com');
|
|
81
|
-
await wrapper.find('.auth-recovery__button').trigger('click');
|
|
82
|
-
await flushPromises();
|
|
83
|
-
expect(h.setErrorState).toHaveBeenCalledWith(err);
|
|
84
|
-
expect(h.closeAuthPopup).not.toHaveBeenCalled();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import AuthRegister from '../register.vue';
|
|
6
|
-
|
|
7
|
-
const h = vi.hoisted(() => {
|
|
8
|
-
const register = vi.fn();
|
|
9
|
-
const closeAuthPopup = vi.fn();
|
|
10
|
-
const showError = vi.fn();
|
|
11
|
-
const showSuccess = vi.fn();
|
|
12
|
-
const setErrorState = vi.fn();
|
|
13
|
-
const resetSnackbar = vi.fn();
|
|
14
|
-
const getRecaptchaToken = vi.fn(() => Promise.resolve('test-token'));
|
|
15
|
-
const initRecaptcha = vi.fn(() => Promise.resolve());
|
|
16
|
-
return {
|
|
17
|
-
register,
|
|
18
|
-
closeAuthPopup,
|
|
19
|
-
showError,
|
|
20
|
-
showSuccess,
|
|
21
|
-
setErrorState,
|
|
22
|
-
resetSnackbar,
|
|
23
|
-
getRecaptchaToken,
|
|
24
|
-
initRecaptcha,
|
|
25
|
-
useUserMock: vi.fn(() => ({ register })),
|
|
26
|
-
useAuthPopupMock: vi.fn(() => ({ closeAuthPopup })),
|
|
27
|
-
useSnackbarMock: vi.fn(() => ({ showError, showSuccess, setErrorState, resetSnackbar })),
|
|
28
|
-
useRecaptchaMock: vi.fn(() => ({ getRecaptchaToken, initRecaptcha })),
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
mockNuxtImport('useUser', () => h.useUserMock);
|
|
33
|
-
mockNuxtImport('useAuthPopup', () => h.useAuthPopupMock);
|
|
34
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
35
|
-
mockNuxtImport('useRecaptcha', () => h.useRecaptchaMock);
|
|
36
|
-
|
|
37
|
-
const VALID = { email: 'user@example.com', username: 'john_doe', password: 'secret' };
|
|
38
|
-
|
|
39
|
-
function mountRegister() {
|
|
40
|
-
return mountSuspended(AuthRegister, {
|
|
41
|
-
global: { stubs: { AuthIcon: true, UiIcon: true } },
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function fillForm(
|
|
46
|
-
wrapper: Awaited<ReturnType<typeof mountRegister>>,
|
|
47
|
-
email: string,
|
|
48
|
-
username: string,
|
|
49
|
-
password: string,
|
|
50
|
-
) {
|
|
51
|
-
await wrapper.find('input[type="email"]').setValue(email);
|
|
52
|
-
await wrapper.find('input[name="login-name"]').setValue(username);
|
|
53
|
-
await wrapper.find('input[type="password"]').setValue(password);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
vi.clearAllMocks();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('AuthRegister', () => {
|
|
61
|
-
it('эмитит login по клику «уже есть аккаунт»', async () => {
|
|
62
|
-
const wrapper = await mountRegister();
|
|
63
|
-
await wrapper.find('.auth-register__login-button').trigger('click');
|
|
64
|
-
expect(wrapper.emitted('login')).toBeTruthy();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('невалидный email → снекбар error_email, register не вызывается', async () => {
|
|
68
|
-
const wrapper = await mountRegister();
|
|
69
|
-
await fillForm(wrapper, 'bad', VALID.username, VALID.password);
|
|
70
|
-
await wrapper.find('.ui-btn').trigger('click');
|
|
71
|
-
await flushPromises();
|
|
72
|
-
expect(h.showError).toHaveBeenCalledWith('error_email');
|
|
73
|
-
expect(h.register).not.toHaveBeenCalled();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('невалидный username → снекбар error_username, register не вызывается', async () => {
|
|
77
|
-
const wrapper = await mountRegister();
|
|
78
|
-
await fillForm(wrapper, VALID.email, 'a', VALID.password);
|
|
79
|
-
await wrapper.find('.ui-btn').trigger('click');
|
|
80
|
-
await flushPromises();
|
|
81
|
-
expect(h.showError).toHaveBeenCalledWith('error_username');
|
|
82
|
-
expect(h.register).not.toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('невалидный password → снекбар error_password, register не вызывается', async () => {
|
|
86
|
-
const wrapper = await mountRegister();
|
|
87
|
-
await fillForm(wrapper, VALID.email, VALID.username, 'abc');
|
|
88
|
-
await wrapper.find('.ui-btn').trigger('click');
|
|
89
|
-
await flushPromises();
|
|
90
|
-
expect(h.showError).toHaveBeenCalledWith('error_password');
|
|
91
|
-
expect(h.register).not.toHaveBeenCalled();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('валидные данные → register(form с reCAPTCHA-токеном), закрытие попапа, снекбар успеха', async () => {
|
|
95
|
-
h.register.mockResolvedValueOnce(undefined);
|
|
96
|
-
const wrapper = await mountRegister();
|
|
97
|
-
await fillForm(wrapper, VALID.email, VALID.username, VALID.password);
|
|
98
|
-
await wrapper.find('.ui-btn').trigger('click');
|
|
99
|
-
await flushPromises();
|
|
100
|
-
expect(h.getRecaptchaToken).toHaveBeenCalledWith('register');
|
|
101
|
-
expect(h.register).toHaveBeenCalledWith({
|
|
102
|
-
email: VALID.email,
|
|
103
|
-
username: VALID.username,
|
|
104
|
-
password_hash: VALID.password,
|
|
105
|
-
token: 'test-token',
|
|
106
|
-
});
|
|
107
|
-
expect(h.closeAuthPopup).toHaveBeenCalled();
|
|
108
|
-
expect(h.showSuccess).toHaveBeenCalledWith('Registration success');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('ошибка register → снекбар ошибки, попап не закрывается', async () => {
|
|
112
|
-
const err = new Error('taken');
|
|
113
|
-
h.register.mockRejectedValueOnce(err);
|
|
114
|
-
const wrapper = await mountRegister();
|
|
115
|
-
await fillForm(wrapper, VALID.email, VALID.username, VALID.password);
|
|
116
|
-
await wrapper.find('.ui-btn').trigger('click');
|
|
117
|
-
await flushPromises();
|
|
118
|
-
expect(h.setErrorState).toHaveBeenCalledWith(err);
|
|
119
|
-
expect(h.closeAuthPopup).not.toHaveBeenCalled();
|
|
120
|
-
expect(h.showSuccess).not.toHaveBeenCalled();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import { VideosApiService } from '../../../services/api/videos.service';
|
|
6
|
-
import { PlaylistsApiService } from '../../../services/api/playlists.service';
|
|
7
|
-
import PlayerLike from '../like.vue';
|
|
8
|
-
|
|
9
|
-
const h = vi.hoisted(() => ({
|
|
10
|
-
executeCalls: [] as Array<{ fn: unknown; args: unknown[] }>,
|
|
11
|
-
useApiActionMock: vi.fn(),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
mockNuxtImport('useApiAction', () => h.useApiActionMock);
|
|
15
|
-
|
|
16
|
-
const MD5 = 'md5abc';
|
|
17
|
-
|
|
18
|
-
function mountLike(props: Record<string, unknown>) {
|
|
19
|
-
return mountSuspended(PlayerLike, {
|
|
20
|
-
props,
|
|
21
|
-
global: { stubs: { UiIcon: true } },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function controls(wrapper: Awaited<ReturnType<typeof mountLike>>) {
|
|
26
|
-
return wrapper.findAll('.player-like__control');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
vi.clearAllMocks();
|
|
31
|
-
h.executeCalls.length = 0;
|
|
32
|
-
localStorage.clear();
|
|
33
|
-
h.useApiActionMock.mockImplementation((fn: unknown) => ({
|
|
34
|
-
execute: (...args: unknown[]) => {
|
|
35
|
-
h.executeCalls.push({ fn, args });
|
|
36
|
-
return Promise.resolve();
|
|
37
|
-
},
|
|
38
|
-
}));
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('PlayerLike', () => {
|
|
42
|
-
it('клик «лайк» (видео) → подсветка, localStorage, videoLike API', async () => {
|
|
43
|
-
const wrapper = await mountLike({ videoMd5: MD5, likes: 5, dislikes: 2 });
|
|
44
|
-
await controls(wrapper)[0].trigger('click');
|
|
45
|
-
await flushPromises();
|
|
46
|
-
expect(controls(wrapper)[0].classes()).toContain('--active');
|
|
47
|
-
expect(localStorage.getItem(MD5)).toBe('like');
|
|
48
|
-
expect(h.executeCalls).toHaveLength(1);
|
|
49
|
-
expect(h.executeCalls[0].fn).toBe(VideosApiService.videoLike);
|
|
50
|
-
expect(h.executeCalls[0].args[0]).toEqual([MD5]);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('клик «дизлайк» (видео) → localStorage, videoDislike API', async () => {
|
|
54
|
-
const wrapper = await mountLike({ videoMd5: MD5, likes: 5, dislikes: 2 });
|
|
55
|
-
await controls(wrapper)[1].trigger('click');
|
|
56
|
-
await flushPromises();
|
|
57
|
-
expect(controls(wrapper)[1].classes()).toContain('--active');
|
|
58
|
-
expect(localStorage.getItem(MD5)).toBe('dislike');
|
|
59
|
-
expect(h.executeCalls[0].fn).toBe(VideosApiService.videoDislike);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('без videoMd5 (плейлист) → playlistLike API с id', async () => {
|
|
63
|
-
const wrapper = await mountLike({ id: 'pl1', likes: 5, dislikes: 2 });
|
|
64
|
-
await controls(wrapper)[0].trigger('click');
|
|
65
|
-
await flushPromises();
|
|
66
|
-
expect(h.executeCalls[0].fn).toBe(PlaylistsApiService.playlistLike);
|
|
67
|
-
expect(h.executeCalls[0].args[0]).toEqual(['pl1']);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('повторный клик той же реакции — no-op (API не зовётся второй раз)', async () => {
|
|
71
|
-
const wrapper = await mountLike({ videoMd5: MD5, likes: 5, dislikes: 2 });
|
|
72
|
-
await controls(wrapper)[0].trigger('click');
|
|
73
|
-
await controls(wrapper)[0].trigger('click');
|
|
74
|
-
await flushPromises();
|
|
75
|
-
expect(h.executeCalls).toHaveLength(1);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('восстанавливает реакцию из localStorage при монтировании', async () => {
|
|
79
|
-
localStorage.setItem(MD5, 'like');
|
|
80
|
-
const wrapper = await mountLike({ videoMd5: MD5, likes: 5, dislikes: 2 });
|
|
81
|
-
await flushPromises();
|
|
82
|
-
expect(controls(wrapper)[0].classes()).toContain('--active');
|
|
83
|
-
expect(h.executeCalls).toHaveLength(0);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport, mockComponent } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import { ref } from 'vue';
|
|
6
|
-
import PlaylistAdd from '../add.vue';
|
|
7
|
-
|
|
8
|
-
const h = vi.hoisted(() => ({
|
|
9
|
-
closePlaylistAdd: vi.fn(),
|
|
10
|
-
setErrorState: vi.fn(),
|
|
11
|
-
showSuccess: vi.fn(),
|
|
12
|
-
showError: vi.fn(),
|
|
13
|
-
usePlaylistAddMock: vi.fn(),
|
|
14
|
-
useFavoritesMock: vi.fn(),
|
|
15
|
-
useSnackbarMock: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
mockNuxtImport('usePlaylistAdd', () => h.usePlaylistAddMock);
|
|
19
|
-
mockNuxtImport('useFavorites', () => h.useFavoritesMock);
|
|
20
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
21
|
-
|
|
22
|
-
mockComponent('UiPopup', () => ({
|
|
23
|
-
template: '<div><slot name="title" /><slot name="fixedContent" /><slot /><slot name="footer" /></div>',
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
const USER_PLAYLISTS = {
|
|
27
|
-
items: [
|
|
28
|
-
{ id: 'p1', name: 'My List', videosCount: 3, playlistType: 0 },
|
|
29
|
-
{ id: 'fav', name: 'Favorites', videosCount: 9, playlistType: 1 },
|
|
30
|
-
],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
let selectValue: ReturnType<typeof ref<string[]>>;
|
|
34
|
-
let step: ReturnType<typeof ref<string>>;
|
|
35
|
-
|
|
36
|
-
function mountAdd() {
|
|
37
|
-
return mountSuspended(PlaylistAdd, {
|
|
38
|
-
props: {
|
|
39
|
-
loadingPostPlaylist: false,
|
|
40
|
-
loadingUserPlaylists: false,
|
|
41
|
-
loadingPostVideo: false,
|
|
42
|
-
userPlaylists: USER_PLAYLISTS,
|
|
43
|
-
},
|
|
44
|
-
global: { stubs: { UiIcon: true, UiToggle: true, UiTooltip: true, CardVideoMini: true } },
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
vi.clearAllMocks();
|
|
50
|
-
selectValue = ref<string[]>([]);
|
|
51
|
-
step = ref('add');
|
|
52
|
-
h.usePlaylistAddMock.mockReturnValue({
|
|
53
|
-
videoCard: ref({ id: 'v1', guid: 'g1', md5: 'm1', thumbNum: 0, title: 'Video', duration: 10 }),
|
|
54
|
-
closePlaylistAdd: h.closePlaylistAdd,
|
|
55
|
-
isPlaylistAdd: ref(true),
|
|
56
|
-
selectValue,
|
|
57
|
-
step,
|
|
58
|
-
onBack: ref(undefined),
|
|
59
|
-
});
|
|
60
|
-
h.useFavoritesMock.mockReturnValue({ FAVORITES_KEY: 'Favorites' });
|
|
61
|
-
h.useSnackbarMock.mockReturnValue({
|
|
62
|
-
setErrorState: h.setErrorState,
|
|
63
|
-
showSuccess: h.showSuccess,
|
|
64
|
-
showError: h.showError,
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
function footerAddButton(wrapper: Awaited<ReturnType<typeof mountAdd>>) {
|
|
69
|
-
return wrapper.findAll('.playlist-add__buttons button')[1];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe('PlaylistAdd', () => {
|
|
73
|
-
it('playlistsItems исключает Favorites', async () => {
|
|
74
|
-
const wrapper = await mountAdd();
|
|
75
|
-
const titles = wrapper.findAll('.playlist-add__item-text').map(n => n.text());
|
|
76
|
-
expect(titles).toEqual(['My List']);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('тоггл «favorites» убирает его из selectValue', async () => {
|
|
80
|
-
const wrapper = await mountAdd();
|
|
81
|
-
expect(selectValue.value).toContain('favorites');
|
|
82
|
-
await wrapper.findAll('input[type="checkbox"]')[0].setValue(false);
|
|
83
|
-
expect(selectValue.value).not.toContain('favorites');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('сабмит (step=add) → эмит save-video-to-playlist, закрытие, снекбар успеха', async () => {
|
|
87
|
-
const wrapper = await mountAdd();
|
|
88
|
-
await footerAddButton(wrapper).trigger('click');
|
|
89
|
-
await flushPromises();
|
|
90
|
-
expect(wrapper.emitted('save-video-to-playlist')).toBeTruthy();
|
|
91
|
-
expect(h.closePlaylistAdd).toHaveBeenCalled();
|
|
92
|
-
expect(h.showSuccess).toHaveBeenCalledWith('video_added');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('новый плейлист с существующим именем → showError, save-video не эмитится', async () => {
|
|
96
|
-
const wrapper = await mountAdd();
|
|
97
|
-
await wrapper.find('.playlist-add__item.--new').trigger('click');
|
|
98
|
-
await wrapper.find('.playlist-input input').setValue('My List');
|
|
99
|
-
await footerAddButton(wrapper).trigger('click');
|
|
100
|
-
await flushPromises();
|
|
101
|
-
expect(h.showError).toHaveBeenCalledWith('playlist_name_exists');
|
|
102
|
-
expect(wrapper.emitted('save-video-to-playlist')).toBeFalsy();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('новый плейлист с уникальным именем → эмит save-video-to-playlist с newPlaylist', async () => {
|
|
106
|
-
const wrapper = await mountAdd();
|
|
107
|
-
await wrapper.find('.playlist-add__item.--new').trigger('click');
|
|
108
|
-
await wrapper.find('.playlist-input input').setValue('Brand New');
|
|
109
|
-
await footerAddButton(wrapper).trigger('click');
|
|
110
|
-
await flushPromises();
|
|
111
|
-
const emitted = wrapper.emitted('save-video-to-playlist');
|
|
112
|
-
expect(emitted).toBeTruthy();
|
|
113
|
-
expect((emitted![0][0] as any).newPlaylist.name).toBe('Brand New');
|
|
114
|
-
});
|
|
115
|
-
});
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport, mockComponent } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import { ref } from 'vue';
|
|
6
|
-
import { PlaylistStep, PlaylistType } from '../../../runtime';
|
|
7
|
-
import PlaylistEdit from '../edit.vue';
|
|
8
|
-
|
|
9
|
-
const h = vi.hoisted(() => ({
|
|
10
|
-
closePlaylistEdit: vi.fn(),
|
|
11
|
-
openPlaylistEdit: vi.fn(),
|
|
12
|
-
showSuccess: vi.fn(),
|
|
13
|
-
usePlaylistEditMock: vi.fn(),
|
|
14
|
-
useSnackbarMock: vi.fn(),
|
|
15
|
-
useStateMock: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
mockNuxtImport('usePlaylistEdit', () => h.usePlaylistEditMock);
|
|
19
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
20
|
-
mockNuxtImport('useState', () => h.useStateMock);
|
|
21
|
-
|
|
22
|
-
mockComponent('UiPopup', () => ({
|
|
23
|
-
template: '<div><slot name="title" /><slot name="fixedContent" /><slot /><slot name="footer" /></div>',
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
let currentStep: ReturnType<typeof ref>;
|
|
27
|
-
|
|
28
|
-
function mountEdit() {
|
|
29
|
-
return mountSuspended(PlaylistEdit, {
|
|
30
|
-
props: {
|
|
31
|
-
loadingPlaylists: false,
|
|
32
|
-
loadingRefresh: false,
|
|
33
|
-
loadingEditVideos: false,
|
|
34
|
-
loadingPlaylistDelete: false,
|
|
35
|
-
},
|
|
36
|
-
global: { stubs: { UiIcon: true, UiInput: true, PlaylistPrivateToggle: true, CardVideoMini: true } },
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
currentStep = ref(PlaylistStep.Edit);
|
|
43
|
-
const states = new Map<string, ReturnType<typeof ref>>();
|
|
44
|
-
h.useStateMock.mockImplementation((key: string) => {
|
|
45
|
-
if (!states.has(key)) states.set(key, ref({ items: [], name: 'My List', playlistType: PlaylistType.Public }));
|
|
46
|
-
return states.get(key);
|
|
47
|
-
});
|
|
48
|
-
h.usePlaylistEditMock.mockReturnValue({
|
|
49
|
-
isPlaylistEditOpen: ref(true),
|
|
50
|
-
closePlaylistEdit: h.closePlaylistEdit,
|
|
51
|
-
currentStep,
|
|
52
|
-
selectedPlaylist: ref({ id: 'p1', name: 'My List', playlistType: PlaylistType.Public }),
|
|
53
|
-
openPlaylistEdit: h.openPlaylistEdit,
|
|
54
|
-
});
|
|
55
|
-
h.useSnackbarMock.mockReturnValue({ showSuccess: h.showSuccess });
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
function primaryButton(wrapper: Awaited<ReturnType<typeof mountEdit>>) {
|
|
59
|
-
return wrapper.findAll('.playlist-edit__buttons button')[1];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
describe('PlaylistEdit', () => {
|
|
63
|
-
it('шаг удаления → onDeleteClick эмитит delete-playlist, снекбар, закрытие', async () => {
|
|
64
|
-
currentStep.value = PlaylistStep.Delete;
|
|
65
|
-
const wrapper = await mountEdit();
|
|
66
|
-
await primaryButton(wrapper).trigger('click');
|
|
67
|
-
await flushPromises();
|
|
68
|
-
expect(wrapper.emitted('delete-playlist')?.[0]).toEqual(['p1']);
|
|
69
|
-
expect(h.showSuccess).toHaveBeenCalledWith('playlist_deleted');
|
|
70
|
-
expect(h.closePlaylistEdit).toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('шаг редактирования эмитит open-edit при открытии', async () => {
|
|
74
|
-
const wrapper = await mountEdit();
|
|
75
|
-
await flushPromises();
|
|
76
|
-
expect(wrapper.emitted('open-edit')?.[0]).toEqual(['p1']);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('шаг редактирования → onSaveClick эмитит save-edit, снекбар, закрытие', async () => {
|
|
80
|
-
const wrapper = await mountEdit();
|
|
81
|
-
await primaryButton(wrapper).trigger('click');
|
|
82
|
-
await flushPromises();
|
|
83
|
-
expect(wrapper.emitted('save-edit')?.[0]).toEqual([{
|
|
84
|
-
id: 'p1',
|
|
85
|
-
playlistName: 'My List',
|
|
86
|
-
playlistType: PlaylistType.Public,
|
|
87
|
-
}]);
|
|
88
|
-
expect(h.showSuccess).toHaveBeenCalledWith('changes_soon');
|
|
89
|
-
expect(h.closePlaylistEdit).toHaveBeenCalled();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import ProfilePassword from '../password.vue';
|
|
6
|
-
|
|
7
|
-
const h = vi.hoisted(() => {
|
|
8
|
-
const changePassword = vi.fn();
|
|
9
|
-
return {
|
|
10
|
-
changePassword,
|
|
11
|
-
useUserMock: vi.fn(() => ({ changePassword })),
|
|
12
|
-
};
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
mockNuxtImport('useUser', () => h.useUserMock);
|
|
16
|
-
|
|
17
|
-
function mountPassword() {
|
|
18
|
-
return mountSuspended(ProfilePassword, {
|
|
19
|
-
global: { stubs: { UiIcon: true } },
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function setPasswords(wrapper: Awaited<ReturnType<typeof mountPassword>>, current: string, next: string) {
|
|
24
|
-
const inputs = wrapper.findAll('input[type="password"]');
|
|
25
|
-
await inputs[0].setValue(current);
|
|
26
|
-
await inputs[1].setValue(next);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
vi.clearAllMocks();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('ProfilePassword', () => {
|
|
34
|
-
it('короткий текущий пароль → поле в ошибке, changePassword не вызывается', async () => {
|
|
35
|
-
const wrapper = await mountPassword();
|
|
36
|
-
await setPasswords(wrapper, 'abc', 'newpass');
|
|
37
|
-
await wrapper.find('.profile-password__button').trigger('click');
|
|
38
|
-
await flushPromises();
|
|
39
|
-
expect(wrapper.findAll('.ui-input')[0].classes()).toContain('ui-input--error');
|
|
40
|
-
expect(h.changePassword).not.toHaveBeenCalled();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('короткий новый пароль → поле в ошибке, changePassword не вызывается', async () => {
|
|
44
|
-
const wrapper = await mountPassword();
|
|
45
|
-
await setPasswords(wrapper, 'oldpass', 'abc');
|
|
46
|
-
await wrapper.find('.profile-password__button').trigger('click');
|
|
47
|
-
await flushPromises();
|
|
48
|
-
expect(wrapper.findAll('.ui-input')[1].classes()).toContain('ui-input--error');
|
|
49
|
-
expect(h.changePassword).not.toHaveBeenCalled();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('оба пароля валидны → changePassword вызывается', async () => {
|
|
53
|
-
const wrapper = await mountPassword();
|
|
54
|
-
await setPasswords(wrapper, 'oldpass', 'newpass');
|
|
55
|
-
await wrapper.find('.profile-password__button').trigger('click');
|
|
56
|
-
await flushPromises();
|
|
57
|
-
// форма очищается в finally (по ссылке), поэтому проверяем факт вызова, не аргументы
|
|
58
|
-
expect(h.changePassword).toHaveBeenCalledTimes(1);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import { ref } from 'vue';
|
|
6
|
-
import ProfileSetup from '../setup.vue';
|
|
7
|
-
|
|
8
|
-
const PROFILE = {
|
|
9
|
-
username: 'john',
|
|
10
|
-
email: 'john@example.com',
|
|
11
|
-
country: '',
|
|
12
|
-
gender: '',
|
|
13
|
-
city: 'NYC',
|
|
14
|
-
birthday: 0,
|
|
15
|
-
avatar: '',
|
|
16
|
-
created: 1700000000,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const h = vi.hoisted(() => ({
|
|
20
|
-
deleteAvatar: vi.fn(() => Promise.resolve()),
|
|
21
|
-
getProfile: vi.fn(() => Promise.resolve()),
|
|
22
|
-
changeEmail: vi.fn(() => Promise.resolve()),
|
|
23
|
-
updateProfile: vi.fn(() => Promise.resolve()),
|
|
24
|
-
showSuccess: vi.fn(),
|
|
25
|
-
generateLink: vi.fn((p: string) => p),
|
|
26
|
-
navigateTo: vi.fn(),
|
|
27
|
-
useStateMock: vi.fn(),
|
|
28
|
-
useUserMock: vi.fn(),
|
|
29
|
-
useSnackbarMock: vi.fn(),
|
|
30
|
-
useGenerateLinkMock: vi.fn(),
|
|
31
|
-
useFetchCountriesMock: vi.fn(),
|
|
32
|
-
useFetchGendersMock: vi.fn(),
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
mockNuxtImport('useState', () => h.useStateMock);
|
|
36
|
-
mockNuxtImport('useUser', () => h.useUserMock);
|
|
37
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
38
|
-
mockNuxtImport('useGenerateLink', () => h.useGenerateLinkMock);
|
|
39
|
-
mockNuxtImport('useFetchDictionariesCountries', () => h.useFetchCountriesMock);
|
|
40
|
-
mockNuxtImport('useFetchDictionariesGenders', () => h.useFetchGendersMock);
|
|
41
|
-
mockNuxtImport('navigateTo', () => h.navigateTo);
|
|
42
|
-
|
|
43
|
-
function mountSetup() {
|
|
44
|
-
return mountSuspended(ProfileSetup, {
|
|
45
|
-
global: { stubs: { UiIcon: true, UiAvatar: true, UiSelect: true, UiToggle: true } },
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
beforeEach(() => {
|
|
50
|
-
vi.clearAllMocks();
|
|
51
|
-
const states = new Map<string, ReturnType<typeof ref>>();
|
|
52
|
-
h.useStateMock.mockImplementation((key: string, init?: () => unknown) => {
|
|
53
|
-
if (key === 'profileData') return ref({ ...PROFILE });
|
|
54
|
-
if (!states.has(key)) states.set(key, ref(typeof init === 'function' ? init() : undefined));
|
|
55
|
-
return states.get(key);
|
|
56
|
-
});
|
|
57
|
-
h.useUserMock.mockReturnValue({
|
|
58
|
-
deleteAvatar: h.deleteAvatar,
|
|
59
|
-
getProfile: h.getProfile,
|
|
60
|
-
changeEmail: h.changeEmail,
|
|
61
|
-
updateProfile: h.updateProfile,
|
|
62
|
-
});
|
|
63
|
-
h.useSnackbarMock.mockReturnValue({ showSuccess: h.showSuccess });
|
|
64
|
-
h.useGenerateLinkMock.mockReturnValue({ generateLink: h.generateLink });
|
|
65
|
-
h.useFetchCountriesMock.mockReturnValue({ countriesData: ref({ countries: [{ title: 'USA' }] }) });
|
|
66
|
-
h.useFetchGendersMock.mockReturnValue({ gendersData: ref({ genders: [{ title: 'Male' }] }) });
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
function clickUpdate(wrapper: Awaited<ReturnType<typeof mountSetup>>) {
|
|
70
|
-
return wrapper.findAll('.profile-setup__button')[1].trigger('click');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
describe('ProfileSetup', () => {
|
|
74
|
-
it('невалидный email → поле в ошибке, updateProfile не вызывается', async () => {
|
|
75
|
-
const wrapper = await mountSetup();
|
|
76
|
-
await wrapper.find('input[type="email"]').setValue('bad');
|
|
77
|
-
await clickUpdate(wrapper);
|
|
78
|
-
await flushPromises();
|
|
79
|
-
expect(wrapper.find('.profile-setup__email-edit').classes()).toContain('ui-input--error');
|
|
80
|
-
expect(h.updateProfile).not.toHaveBeenCalled();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('валидный email без изменения → updateProfile вызван, changeEmail нет', async () => {
|
|
84
|
-
const wrapper = await mountSetup();
|
|
85
|
-
await clickUpdate(wrapper);
|
|
86
|
-
await flushPromises();
|
|
87
|
-
expect(h.updateProfile).toHaveBeenCalledTimes(1);
|
|
88
|
-
expect(h.changeEmail).not.toHaveBeenCalled();
|
|
89
|
-
expect(h.navigateTo).toHaveBeenCalled();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('изменённый email → changeEmail и updateProfile вызваны', async () => {
|
|
93
|
-
const wrapper = await mountSetup();
|
|
94
|
-
await wrapper.find('input[type="email"]').setValue('new@example.com');
|
|
95
|
-
await clickUpdate(wrapper);
|
|
96
|
-
await flushPromises();
|
|
97
|
-
expect(h.changeEmail).toHaveBeenCalledWith({ email: 'new@example.com' });
|
|
98
|
-
expect(h.updateProfile).toHaveBeenCalledTimes(1);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mountSuspended, mockNuxtImport, mockComponent } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import { ref } from 'vue';
|
|
6
|
-
import { reportFormsScheme } from '../../../lib';
|
|
7
|
-
import { ReportFormsSubjects } from '../../../runtime';
|
|
8
|
-
import ReportMain from '../main.vue';
|
|
9
|
-
|
|
10
|
-
const h = vi.hoisted(() => ({
|
|
11
|
-
closeReportPopup: vi.fn(),
|
|
12
|
-
showSuccess: vi.fn(),
|
|
13
|
-
showError: vi.fn(),
|
|
14
|
-
resetSnackbar: vi.fn(),
|
|
15
|
-
getRecaptchaToken: vi.fn(() => Promise.resolve('test-token')),
|
|
16
|
-
initRecaptcha: vi.fn(() => Promise.resolve()),
|
|
17
|
-
useReportPopupMock: vi.fn(),
|
|
18
|
-
useSnackbarMock: vi.fn(),
|
|
19
|
-
useRecaptchaMock: vi.fn(),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
mockNuxtImport('useReportPopup', () => h.useReportPopupMock);
|
|
23
|
-
mockNuxtImport('useSnackbar', () => h.useSnackbarMock);
|
|
24
|
-
mockNuxtImport('useRecaptcha', () => h.useRecaptchaMock);
|
|
25
|
-
|
|
26
|
-
mockComponent('UiPopup', () => ({
|
|
27
|
-
template: '<div><slot name="title" /><slot name="fixedContent" /><slot /><slot name="footer" /></div>',
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
const WRONG_INDEX = reportFormsScheme.findIndex(c => c.subject === ReportFormsSubjects.Wrong);
|
|
31
|
-
|
|
32
|
-
function mountReport() {
|
|
33
|
-
return mountSuspended(ReportMain, {
|
|
34
|
-
global: {
|
|
35
|
-
stubs: {
|
|
36
|
-
CardVideoMini: true,
|
|
37
|
-
UiIcon: true,
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
vi.clearAllMocks();
|
|
45
|
-
h.useReportPopupMock.mockReturnValue({
|
|
46
|
-
isReportPopupOpen: ref(true),
|
|
47
|
-
reportedVideoCard: ref({ guid: 'g123', id: 'vid1' }),
|
|
48
|
-
closeReportPopup: h.closeReportPopup,
|
|
49
|
-
onBack: ref(undefined),
|
|
50
|
-
});
|
|
51
|
-
h.useSnackbarMock.mockReturnValue({
|
|
52
|
-
showSuccess: h.showSuccess,
|
|
53
|
-
showError: h.showError,
|
|
54
|
-
resetSnackbar: h.resetSnackbar,
|
|
55
|
-
});
|
|
56
|
-
h.useRecaptchaMock.mockReturnValue({
|
|
57
|
-
getRecaptchaToken: h.getRecaptchaToken,
|
|
58
|
-
initRecaptcha: h.initRecaptcha,
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
async function selectWrong(wrapper: Awaited<ReturnType<typeof mountReport>>) {
|
|
63
|
-
await wrapper.findAll('.report-main__category-button')[WRONG_INDEX].trigger('click');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
describe('ReportMain', () => {
|
|
67
|
-
it('первый шаг — список всех категорий', async () => {
|
|
68
|
-
const wrapper = await mountReport();
|
|
69
|
-
expect(wrapper.findAll('.report-main__category-button')).toHaveLength(reportFormsScheme.length);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('выбор категории переключает на форму (кнопка отправки появляется)', async () => {
|
|
73
|
-
const wrapper = await mountReport();
|
|
74
|
-
await selectWrong(wrapper);
|
|
75
|
-
expect(wrapper.find('.report-main__category-button').exists()).toBe(false);
|
|
76
|
-
expect(wrapper.find('.report-main__send-button').exists()).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('пустой сабмит → снекбар ошибки, submit не эмитится, попап не закрывается', async () => {
|
|
80
|
-
const wrapper = await mountReport();
|
|
81
|
-
await selectWrong(wrapper);
|
|
82
|
-
await wrapper.find('.report-main__send-button').trigger('click');
|
|
83
|
-
await flushPromises();
|
|
84
|
-
expect(h.showError).toHaveBeenCalled();
|
|
85
|
-
expect(wrapper.emitted('submit')).toBeFalsy();
|
|
86
|
-
expect(h.closeReportPopup).not.toHaveBeenCalled();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('валидный сабмит → эмитит submit с guid, закрывает попап, снекбар успеха', async () => {
|
|
90
|
-
const wrapper = await mountReport();
|
|
91
|
-
await selectWrong(wrapper);
|
|
92
|
-
await wrapper.find('input[type="radio"]').trigger('change');
|
|
93
|
-
await wrapper.find('input[type="email"]').setValue('reporter@example.com');
|
|
94
|
-
await wrapper.find('.report-main__send-button').trigger('click');
|
|
95
|
-
await flushPromises();
|
|
96
|
-
|
|
97
|
-
// submit эмитится только при валидной форме (пустой сабмит выше его не эмитит),
|
|
98
|
-
// поэтому сам факт эмита подтверждает, что email прошёл валидацию.
|
|
99
|
-
const submitted = wrapper.emitted('submit');
|
|
100
|
-
expect(submitted).toBeTruthy();
|
|
101
|
-
expect(submitted![0][0]).toMatchObject({ guid: 'g123' });
|
|
102
|
-
expect(h.getRecaptchaToken).toHaveBeenCalledWith('contact_form');
|
|
103
|
-
expect(h.closeReportPopup).toHaveBeenCalled();
|
|
104
|
-
expect(h.showSuccess).toHaveBeenCalledWith('email_send');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
import { mockNuxtImport, mountSuspended } from '@nuxt/test-utils/runtime';
|
|
4
|
-
import { flushPromises } from '@vue/test-utils';
|
|
5
|
-
import { defineComponent, h } from 'vue';
|
|
6
|
-
import { useNextVideo } from '../use-next-video';
|
|
7
|
-
|
|
8
|
-
// Мокаем только безопасное: useNuxtApp/useState/useRoute трогать нельзя — их использует
|
|
9
|
-
// сам mountSuspended. $features (FetchByUrlEnabled: false → cardKey='id') и isMobile
|
|
10
|
-
// берутся реальные. route мокать можно (это не ядро test-utils).
|
|
11
|
-
const hoisted = vi.hoisted(() => ({
|
|
12
|
-
route: { query: {} as Record<string, string>, path: '/videos/landing' },
|
|
13
|
-
navigateTo: vi.fn(),
|
|
14
|
-
fetchRelated: vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
mockNuxtImport('useRoute', () => () => hoisted.route);
|
|
18
|
-
mockNuxtImport('useLang', () => () => 'en');
|
|
19
|
-
mockNuxtImport('useSlug', () => () => ({ value: 'landing' }));
|
|
20
|
-
mockNuxtImport('useApiAction', () => () => ({ execute: hoisted.fetchRelated }));
|
|
21
|
-
mockNuxtImport('navigateTo', () => hoisted.navigateTo);
|
|
22
|
-
|
|
23
|
-
function card(id: string) {
|
|
24
|
-
return { id, guid: id, url: `url-${id}`, title: `Video ${id}`, duration: 10, thumbUrls: {} };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Лист 1 из 2 страниц: [a, b].
|
|
28
|
-
const page1 = { currentPage: 1, allPages: 2, total: 4, items: [card('a'), card('b')] };
|
|
29
|
-
|
|
30
|
-
async function mountHost(props: { data: any; suggested: any; basePath?: string }) {
|
|
31
|
-
const Host = defineComponent({
|
|
32
|
-
setup() {
|
|
33
|
-
return useNextVideo({
|
|
34
|
-
data: () => props.data,
|
|
35
|
-
suggestedVideos: () => props.suggested,
|
|
36
|
-
basePath: () => props.basePath,
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
render: () => h('div'),
|
|
40
|
-
});
|
|
41
|
-
return mountSuspended(Host);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
beforeEach(() => {
|
|
45
|
-
vi.clearAllMocks();
|
|
46
|
-
hoisted.route = { query: {}, path: '/videos/landing' };
|
|
47
|
-
// Следующая страница related: [c, d].
|
|
48
|
-
hoisted.fetchRelated.mockResolvedValue({ currentPage: 2, allPages: 2, total: 4, items: [card('c'), card('d')] });
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('useNextVideo', () => {
|
|
52
|
-
it('на лендинге next = первый ролик листа', async () => {
|
|
53
|
-
const wrapper = await mountHost({ data: card('landing'), suggested: page1 });
|
|
54
|
-
expect(wrapper.vm.nextVideo.id).toBe('a');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('в середине листа next = следующий', async () => {
|
|
58
|
-
hoisted.route = { query: { view: 'a' }, path: '/videos/landing' };
|
|
59
|
-
const wrapper = await mountHost({ data: card('a'), suggested: page1 });
|
|
60
|
-
expect(wrapper.vm.nextVideo.id).toBe('b');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('на последнем в листе догружает следующую страницу, next = её первый ролик', async () => {
|
|
64
|
-
hoisted.route = { query: { view: 'b' }, path: '/videos/landing' };
|
|
65
|
-
const wrapper = await mountHost({ data: card('b'), suggested: page1 });
|
|
66
|
-
await flushPromises();
|
|
67
|
-
|
|
68
|
-
expect(hoisted.fetchRelated).toHaveBeenCalledWith([{ page: 2, 'per-page': 14 }, 'en', 'landing']);
|
|
69
|
-
expect(wrapper.vm.nextVideo.id).toBe('c');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('goNext в середине листа — только ?view', async () => {
|
|
73
|
-
hoisted.route = { query: { view: 'a' }, path: '/videos/landing' };
|
|
74
|
-
const wrapper = await mountHost({ data: card('a'), suggested: page1 });
|
|
75
|
-
|
|
76
|
-
wrapper.vm.goNext({ direction: 'next' });
|
|
77
|
-
expect(hoisted.navigateTo).toHaveBeenCalledWith({ path: '/videos/landing', query: { view: 'b' } });
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('goNext через границу листа — ?view + ?page', async () => {
|
|
81
|
-
hoisted.route = { query: { view: 'b' }, path: '/videos/landing' };
|
|
82
|
-
const wrapper = await mountHost({ data: card('b'), suggested: page1 });
|
|
83
|
-
await flushPromises();
|
|
84
|
-
|
|
85
|
-
wrapper.vm.goNext({ direction: 'next' });
|
|
86
|
-
expect(hoisted.navigateTo).toHaveBeenCalledWith({ path: '/videos/landing', query: { view: 'c', page: '2' } });
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('в самом конце всех страниц next зацикливается на первый', async () => {
|
|
90
|
-
const single = { currentPage: 1, allPages: 1, total: 2, items: [card('a'), card('b')] };
|
|
91
|
-
hoisted.route = { query: { view: 'b' }, path: '/videos/landing' };
|
|
92
|
-
const wrapper = await mountHost({ data: card('b'), suggested: single });
|
|
93
|
-
await flushPromises();
|
|
94
|
-
|
|
95
|
-
expect(hoisted.fetchRelated).not.toHaveBeenCalled();
|
|
96
|
-
expect(wrapper.vm.nextVideo.id).toBe('a');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('плейлист — реальный переход на id следующего ролика', async () => {
|
|
100
|
-
const upcoming = { currentPage: 1, allPages: 1, total: 2, items: [card('x'), card('y')] };
|
|
101
|
-
const wrapper = await mountHost({ data: card('cur'), suggested: upcoming, basePath: '/playlists/p1' });
|
|
102
|
-
|
|
103
|
-
wrapper.vm.goNext({ direction: 'next' });
|
|
104
|
-
expect(hoisted.navigateTo).toHaveBeenCalledWith('/playlists/p1/x');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('плейлист не догружает related', async () => {
|
|
108
|
-
const upcoming = { currentPage: 1, allPages: 5, total: 2, items: [card('x'), card('y')] };
|
|
109
|
-
hoisted.route = { query: {}, path: '/playlists/p1/cur' };
|
|
110
|
-
await mountHost({ data: card('cur'), suggested: upcoming, basePath: '/playlists/p1' });
|
|
111
|
-
await flushPromises();
|
|
112
|
-
|
|
113
|
-
expect(hoisted.fetchRelated).not.toHaveBeenCalled();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('direction=previous игнорируется', async () => {
|
|
117
|
-
hoisted.route = { query: { view: 'a' }, path: '/videos/landing' };
|
|
118
|
-
const wrapper = await mountHost({ data: card('a'), suggested: page1 });
|
|
119
|
-
|
|
120
|
-
wrapper.vm.goNext({ direction: 'previous' });
|
|
121
|
-
expect(hoisted.navigateTo).not.toHaveBeenCalled();
|
|
122
|
-
});
|
|
123
|
-
});
|