mirror of
https://github.com/unraid/api.git
synced 2026-01-05 16:09:49 -06:00
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - Documentation - Updated contributor guide to use “standalone” naming for web components. - Refactor - Migrated app and component references from legacy variants to standalone components. - Unified component registry and updated global component typings to standalone names. - Tests - Updated test suites to target standalone components; no behavior changes. No user-facing changes. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
244 lines
7.3 KiB
TypeScript
244 lines
7.3 KiB
TypeScript
/**
|
|
* Registration Component Test Coverage
|
|
*/
|
|
|
|
import { setActivePinia } from 'pinia';
|
|
import { mount } from '@vue/test-utils';
|
|
|
|
import { createTestingPinia } from '@pinia/testing';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import type { VueWrapper } from '@vue/test-utils';
|
|
import type { ServerconnectPluginInstalled } from '~/types/server';
|
|
import type { Pinia } from 'pinia';
|
|
|
|
import Registration from '~/components/Registration.standalone.vue';
|
|
import { usePurchaseStore } from '~/store/purchase';
|
|
import { useReplaceRenewStore } from '~/store/replaceRenew';
|
|
import { useServerStore } from '~/store/server';
|
|
|
|
vi.mock('crypto-js/aes.js', () => ({ default: {} }));
|
|
|
|
vi.mock('@unraid/shared-callbacks', () => ({
|
|
useCallback: vi.fn(() => ({
|
|
send: vi.fn(),
|
|
watcher: vi.fn(),
|
|
})),
|
|
}));
|
|
|
|
vi.mock('@vue/apollo-composable', () => ({
|
|
useQuery: () => ({
|
|
result: { value: {} },
|
|
loading: { value: false },
|
|
}),
|
|
useLazyQuery: () => ({
|
|
result: { value: {} },
|
|
loading: { value: false },
|
|
load: vi.fn(),
|
|
refetch: vi.fn(),
|
|
onResult: vi.fn(),
|
|
onError: vi.fn(),
|
|
}),
|
|
useMutation: () => ({
|
|
mutate: vi.fn(),
|
|
onDone: vi.fn(),
|
|
onError: vi.fn(),
|
|
}),
|
|
provideApolloClient: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@unraid/ui', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@unraid/ui')>();
|
|
|
|
return {
|
|
...actual,
|
|
BrandButton: { template: '<button><slot /></button>', props: ['text', 'title', 'icon', 'disabled'] },
|
|
CardWrapper: { template: '<div><slot /></div>' },
|
|
PageContainer: { template: '<div><slot /></div>' },
|
|
SettingsGrid: { template: '<div class="settings-grid"><slot /></div>' },
|
|
};
|
|
});
|
|
|
|
vi.mock('~/components/KeyActions.vue', () => ({
|
|
default: { template: '<div data-testid="key-actions"><slot/></div>', props: ['t', 'filterOut'] },
|
|
}));
|
|
|
|
vi.mock('~/components/Registration/KeyLinkedStatus.vue', () => ({
|
|
default: { template: '<div data-testid="key-linked-status"></div>', props: ['t'] },
|
|
}));
|
|
|
|
vi.mock('~/components/Registration/ReplaceCheck.vue', () => ({
|
|
default: { template: '<div data-testid="replace-check"></div>', props: ['t'] },
|
|
}));
|
|
|
|
vi.mock('~/components/Registration/UpdateExpirationAction.vue', () => ({
|
|
default: { template: '<div data-testid="update-expiration"></div>', props: ['t'] },
|
|
}));
|
|
|
|
vi.mock('~/components/UserProfile/UptimeExpire.vue', () => ({
|
|
default: {
|
|
template: '<div data-testid="uptime-expire"></div>',
|
|
props: ['t', 'forExpire', 'shortText'],
|
|
},
|
|
}));
|
|
|
|
// Define initial state for the server store for testing
|
|
const initialServerState = {
|
|
dateTimeFormat: { date: 'MMM D, YYYY', time: 'h:mm A' },
|
|
deviceCount: 0,
|
|
guid: '',
|
|
flashVendor: '',
|
|
flashProduct: '',
|
|
keyfile: '',
|
|
regGuid: '',
|
|
regTm: '',
|
|
regTo: '',
|
|
regTy: '',
|
|
regExp: null,
|
|
regUpdatesExpired: false,
|
|
serverErrors: [],
|
|
state: 'ENOKEYFILE',
|
|
stateData: { heading: 'Default Heading', message: 'Default Message' },
|
|
stateDataError: false,
|
|
tooManyDevices: false,
|
|
};
|
|
|
|
const mockFormattedDateTime = vi.fn(() => 'Formatted Date');
|
|
vi.mock('~/composables/dateTime', () => ({
|
|
default: vi.fn(() => ({
|
|
outputDateTimeFormatted: { value: mockFormattedDateTime() },
|
|
})),
|
|
}));
|
|
|
|
const t = (key: string) => key;
|
|
|
|
vi.mock('vue-i18n', () => ({
|
|
useI18n: () => ({ t }),
|
|
}));
|
|
|
|
describe('Registration.standalone.vue', () => {
|
|
let wrapper: VueWrapper<unknown>;
|
|
let pinia: Pinia;
|
|
let serverStore: ReturnType<typeof useServerStore>;
|
|
let replaceRenewStore: ReturnType<typeof useReplaceRenewStore>;
|
|
let purchaseStore: ReturnType<typeof usePurchaseStore>;
|
|
|
|
const findItemByLabel = (labelKey: string) => {
|
|
const allLabels = wrapper.findAll('.font-semibold');
|
|
const label = allLabels.find((el) => el.html().includes(t(labelKey)));
|
|
|
|
if (!label) return undefined;
|
|
|
|
const nextSibling = label.element.nextElementSibling;
|
|
|
|
return {
|
|
exists: () => true,
|
|
props: (prop: string) => {
|
|
if (prop === 'text' && nextSibling) {
|
|
return nextSibling.textContent?.trim();
|
|
}
|
|
return undefined;
|
|
},
|
|
};
|
|
};
|
|
|
|
beforeEach(() => {
|
|
pinia = createTestingPinia({
|
|
createSpy: vi.fn,
|
|
initialState: {
|
|
server: { ...initialServerState },
|
|
},
|
|
stubActions: true,
|
|
});
|
|
setActivePinia(pinia);
|
|
|
|
serverStore = useServerStore();
|
|
replaceRenewStore = useReplaceRenewStore();
|
|
purchaseStore = usePurchaseStore();
|
|
|
|
serverStore.deprecatedUnraidSSL = undefined;
|
|
|
|
replaceRenewStore.check = vi.fn();
|
|
|
|
vi.clearAllMocks();
|
|
|
|
// Mount after store setup
|
|
wrapper = mount(Registration, {
|
|
global: {
|
|
plugins: [pinia],
|
|
stubs: {
|
|
ShieldCheckIcon: { template: '<div class="shield-check-icon"/>' },
|
|
ShieldExclamationIcon: { template: '<div class="shield-exclamation-icon"/>' },
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
wrapper?.unmount();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('renders default heading and message when state is ENOKEYFILE', () => {
|
|
const heading = wrapper.find('h3');
|
|
const subheading = wrapper.find('.prose');
|
|
|
|
expect(heading.text()).toContain("Let's Unleash Your Hardware");
|
|
expect(subheading.text()).toContain('Choose an option below');
|
|
expect(findItemByLabel(t('License key type'))).toBeUndefined();
|
|
expect(findItemByLabel(t('Flash GUID'))).toBeUndefined();
|
|
expect(wrapper.find('[data-testid="key-actions"]').exists()).toBe(true);
|
|
expect(wrapper.find('[data-testid="replace-check"]').exists()).toBe(false);
|
|
expect(wrapper.find('[data-testid="key-linked-status"]').exists()).toBe(false);
|
|
});
|
|
|
|
it('triggers expected action when key action is clicked', async () => {
|
|
serverStore.state = 'TRIAL';
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const keyActionsElement = wrapper.find('[data-testid="key-actions"]');
|
|
|
|
expect(keyActionsElement.exists(), 'KeyActions element not found').toBe(true);
|
|
|
|
const expectedActions = serverStore.keyActions?.filter((action) => !['renew'].includes(action.name));
|
|
|
|
expect(expectedActions, 'No expected actions found in store for TRIAL state').toBeDefined();
|
|
expect(expectedActions!.length).toBeGreaterThan(0);
|
|
|
|
const purchaseAction = expectedActions!.find((a) => a.name === 'purchase');
|
|
|
|
expect(purchaseAction, 'Purchase action not found in expected actions').toBeDefined();
|
|
|
|
purchaseAction!.click?.();
|
|
|
|
expect(purchaseStore.purchase).toHaveBeenCalled();
|
|
});
|
|
|
|
it('renders registered state information when state is PRO', async () => {
|
|
serverStore.state = 'PRO';
|
|
serverStore.regTy = 'Pro';
|
|
serverStore.regTo = 'Test User';
|
|
serverStore.regGuid = '12345-ABCDE';
|
|
serverStore.registered = true;
|
|
serverStore.connectPluginInstalled = 'INSTALLED' as ServerconnectPluginInstalled;
|
|
serverStore.guid = 'FLASH-GUID-123';
|
|
serverStore.deviceCount = 5;
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const keyTypeItem = findItemByLabel(t('License key type'));
|
|
|
|
expect(keyTypeItem).toBeDefined();
|
|
expect(keyTypeItem?.props('text')).toBe('Pro');
|
|
|
|
const registeredToItem = findItemByLabel(t('Registered to'));
|
|
|
|
expect(registeredToItem).toBeDefined();
|
|
expect(registeredToItem?.props('text')).toBe('Test User');
|
|
expect(findItemByLabel(t('Flash GUID'))).toBeDefined();
|
|
expect(findItemByLabel(t('Attached Storage Devices'))).toBeDefined();
|
|
expect(wrapper.find('[data-testid="key-actions"]').exists()).toBe(false);
|
|
});
|
|
});
|