184 lines
7.8 KiB
TypeScript
184 lines
7.8 KiB
TypeScript
|
|
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||
|
|
|
||
|
|
import { useLicenseStore } from './licenseStore';
|
||
|
|
|
||
|
|
|
||
|
|
function mockElectronAPI(overrides: Record<string, any> = {}) {
|
||
|
|
(window as any).electronAPI = {
|
||
|
|
getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }),
|
||
|
|
activateLicense: vi.fn().mockResolvedValue(null),
|
||
|
|
deactivateLicense: vi.fn().mockResolvedValue(undefined),
|
||
|
|
hasLicenseFeature: vi.fn().mockResolvedValue(false),
|
||
|
|
...overrides,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('licenseStore', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
mockElectronAPI();
|
||
|
|
useLicenseStore.setState({ status: null, isLoaded: false, canEdit: true, showDialog: false });
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('canEdit', () => {
|
||
|
|
test('is true for Licensed status', async () => {
|
||
|
|
mockElectronAPI({
|
||
|
|
getAppStatus: vi.fn().mockResolvedValue({ tag: 'Licensed', license: { license_id: 'x', tier: 'pro' } }),
|
||
|
|
});
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('is true for Trial status', async () => {
|
||
|
|
mockElectronAPI({
|
||
|
|
getAppStatus: vi.fn().mockResolvedValue({ tag: 'Trial', days_remaining: 20, started_at: Date.now() }),
|
||
|
|
});
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('is false for Expired status', async () => {
|
||
|
|
mockElectronAPI({
|
||
|
|
getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }),
|
||
|
|
});
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('is false when status is null', () => {
|
||
|
|
useLicenseStore.setState({ status: null, canEdit: true });
|
||
|
|
useLicenseStore.getState().setStatus(null);
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('checkStatus', () => {
|
||
|
|
test('sets status to Licensed when backend returns Licensed', async () => {
|
||
|
|
const license = { license_id: 'l1', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 };
|
||
|
|
mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Licensed', license }) });
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Licensed');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sets status to Trial when backend returns Trial', async () => {
|
||
|
|
mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Trial', days_remaining: 15, started_at: Date.now() }) });
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Trial');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sets status to Expired when backend returns Expired', async () => {
|
||
|
|
mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }) });
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('handles API error gracefully', async () => {
|
||
|
|
mockElectronAPI({ getAppStatus: vi.fn().mockRejectedValue(new Error('network error')) });
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('handles missing electronAPI', async () => {
|
||
|
|
delete (window as any).electronAPI;
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sets isLoaded to true after check', async () => {
|
||
|
|
await useLicenseStore.getState().checkStatus();
|
||
|
|
expect(useLicenseStore.getState().isLoaded).toBe(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('activateLicense', () => {
|
||
|
|
test('sets Licensed on valid key', async () => {
|
||
|
|
const license = { license_id: 'l2', tier: 'pro', customer_email: 'x@y.com', expires_at: 9999999999, features: ['bg_removal'], issued_at: 1, max_activations: 1 };
|
||
|
|
mockElectronAPI({ activateLicense: vi.fn().mockResolvedValue(license) });
|
||
|
|
const result = await useLicenseStore.getState().activateLicense('talkedit_v1_validKey');
|
||
|
|
expect(result).toBe(true);
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Licensed');
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('returns false on invalid key', async () => {
|
||
|
|
mockElectronAPI({ activateLicense: vi.fn().mockResolvedValue(null) });
|
||
|
|
const result = await useLicenseStore.getState().activateLicense('invalid-key');
|
||
|
|
expect(result).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('returns false on API error', async () => {
|
||
|
|
mockElectronAPI({ activateLicense: vi.fn().mockRejectedValue(new Error('bad key')) });
|
||
|
|
const result = await useLicenseStore.getState().activateLicense('bad-key');
|
||
|
|
expect(result).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('closes dialog on success', async () => {
|
||
|
|
useLicenseStore.setState({ showDialog: true });
|
||
|
|
const license = { license_id: 'l3', tier: 'business', customer_email: 'z@z.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 5 };
|
||
|
|
mockElectronAPI({ activateLicense: vi.fn().mockResolvedValue(license) });
|
||
|
|
await useLicenseStore.getState().activateLicense('talkedit_v1_key');
|
||
|
|
expect(useLicenseStore.getState().showDialog).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('deactivateLicense', () => {
|
||
|
|
test('sets Expired when trial is over', async () => {
|
||
|
|
mockElectronAPI({
|
||
|
|
deactivateLicense: vi.fn().mockResolvedValue(undefined),
|
||
|
|
getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }),
|
||
|
|
});
|
||
|
|
await useLicenseStore.getState().deactivateLicense();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('restores Trial when trial is still valid', async () => {
|
||
|
|
mockElectronAPI({
|
||
|
|
deactivateLicense: vi.fn().mockResolvedValue(undefined),
|
||
|
|
getAppStatus: vi.fn().mockResolvedValue({ tag: 'Trial', days_remaining: 5, started_at: Date.now() }),
|
||
|
|
});
|
||
|
|
await useLicenseStore.getState().deactivateLicense();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Trial');
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('handles API error', async () => {
|
||
|
|
mockElectronAPI({ deactivateLicense: vi.fn().mockRejectedValue(new Error('fail')) });
|
||
|
|
useLicenseStore.setState({ status: { tag: 'Licensed', license: {} as any }, canEdit: true });
|
||
|
|
await useLicenseStore.getState().deactivateLicense();
|
||
|
|
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||
|
|
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('hasFeature', () => {
|
||
|
|
test('returns true when feature exists', async () => {
|
||
|
|
mockElectronAPI({ hasLicenseFeature: vi.fn().mockResolvedValue(true) });
|
||
|
|
const result = await useLicenseStore.getState().hasFeature('bg_removal');
|
||
|
|
expect(result).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('returns false when feature missing', async () => {
|
||
|
|
mockElectronAPI({ hasLicenseFeature: vi.fn().mockResolvedValue(false) });
|
||
|
|
const result = await useLicenseStore.getState().hasFeature('nonexistent');
|
||
|
|
expect(result).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('returns false on API error', async () => {
|
||
|
|
mockElectronAPI({ hasLicenseFeature: vi.fn().mockRejectedValue(new Error('fail')) });
|
||
|
|
const result = await useLicenseStore.getState().hasFeature('bg_removal');
|
||
|
|
expect(result).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('setShowDialog', () => {
|
||
|
|
test('toggles dialog', () => {
|
||
|
|
useLicenseStore.getState().setShowDialog(true);
|
||
|
|
expect(useLicenseStore.getState().showDialog).toBe(true);
|
||
|
|
useLicenseStore.getState().setShowDialog(false);
|
||
|
|
expect(useLicenseStore.getState().showDialog).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|