Gate AI editing features behind license (trial users no longer get AI editing)
This commit is contained in:
@ -16,7 +16,7 @@ function mockElectronAPI(overrides: Record<string, any> = {}) {
|
||||
describe('licenseStore', () => {
|
||||
beforeEach(() => {
|
||||
mockElectronAPI();
|
||||
useLicenseStore.setState({ status: null, isLoaded: false, canEdit: true, showDialog: false });
|
||||
useLicenseStore.setState({ status: null, isLoaded: false, canEdit: true, canUseAI: false, showDialog: false });
|
||||
});
|
||||
|
||||
describe('canEdit', () => {
|
||||
@ -45,10 +45,28 @@ describe('licenseStore', () => {
|
||||
});
|
||||
|
||||
test('is false when status is null', () => {
|
||||
useLicenseStore.setState({ status: null, canEdit: true });
|
||||
useLicenseStore.setState({ status: null, canEdit: true, canUseAI: false });
|
||||
useLicenseStore.getState().setStatus(null);
|
||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||
});
|
||||
|
||||
test('is true for Licensed status', () => {
|
||||
useLicenseStore.getState().setStatus({ tag: 'Licensed', license: { license_id: 'x', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 } });
|
||||
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(true);
|
||||
});
|
||||
|
||||
test('is false for Trial status', () => {
|
||||
useLicenseStore.getState().setStatus({ tag: 'Trial', days_remaining: 5, started_at: Date.now() });
|
||||
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
|
||||
test('is false for Expired status', () => {
|
||||
useLicenseStore.getState().setStatus({ tag: 'Expired' });
|
||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkStatus', () => {
|
||||
@ -76,6 +94,7 @@ describe('licenseStore', () => {
|
||||
await useLicenseStore.getState().checkStatus();
|
||||
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
|
||||
test('handles missing electronAPI', async () => {
|
||||
@ -83,6 +102,7 @@ describe('licenseStore', () => {
|
||||
await useLicenseStore.getState().checkStatus();
|
||||
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
|
||||
test('sets isLoaded to true after check', async () => {
|
||||
@ -99,6 +119,7 @@ describe('licenseStore', () => {
|
||||
expect(result).toBe(true);
|
||||
expect(useLicenseStore.getState().status?.tag).toBe('Licensed');
|
||||
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(true);
|
||||
});
|
||||
|
||||
test('returns false on invalid key', async () => {
|
||||
@ -131,6 +152,7 @@ describe('licenseStore', () => {
|
||||
await useLicenseStore.getState().deactivateLicense();
|
||||
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
|
||||
test('restores Trial when trial is still valid', async () => {
|
||||
@ -141,14 +163,16 @@ describe('licenseStore', () => {
|
||||
await useLicenseStore.getState().deactivateLicense();
|
||||
expect(useLicenseStore.getState().status?.tag).toBe('Trial');
|
||||
expect(useLicenseStore.getState().canEdit).toBe(true);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
|
||||
test('handles API error', async () => {
|
||||
mockElectronAPI({ deactivateLicense: vi.fn().mockRejectedValue(new Error('fail')) });
|
||||
useLicenseStore.setState({ status: { tag: 'Licensed', license: {} as any }, canEdit: true });
|
||||
useLicenseStore.setState({ status: { tag: 'Licensed', license: {} as any }, canEdit: true, canUseAI: true });
|
||||
await useLicenseStore.getState().deactivateLicense();
|
||||
expect(useLicenseStore.getState().status?.tag).toBe('Expired');
|
||||
expect(useLicenseStore.getState().canEdit).toBe(false);
|
||||
expect(useLicenseStore.getState().canUseAI).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ interface LicenseState {
|
||||
isLoaded: boolean;
|
||||
showDialog: boolean;
|
||||
canEdit: boolean;
|
||||
canUseAI: boolean;
|
||||
}
|
||||
|
||||
interface LicenseActions {
|
||||
@ -43,10 +44,12 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
||||
isLoaded: false,
|
||||
showDialog: false,
|
||||
canEdit: false,
|
||||
canUseAI: false,
|
||||
|
||||
setStatus: (status) => {
|
||||
const canEdit = status?.tag === 'Licensed' || status?.tag === 'Trial';
|
||||
set({ status, isLoaded: true, canEdit });
|
||||
const canUseAI = status?.tag === 'Licensed';
|
||||
set({ status, isLoaded: true, canEdit, canUseAI });
|
||||
},
|
||||
|
||||
setShowDialog: (show) => set({ showDialog: show }),
|
||||
@ -55,9 +58,10 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
||||
try {
|
||||
const status = await window.electronAPI?.getAppStatus();
|
||||
const canEdit = status?.tag === 'Licensed' || status?.tag === 'Trial';
|
||||
set({ status: status || { tag: 'Expired' }, isLoaded: true, canEdit });
|
||||
const canUseAI = status?.tag === 'Licensed';
|
||||
set({ status: status || { tag: 'Expired' }, isLoaded: true, canEdit, canUseAI });
|
||||
} catch {
|
||||
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false });
|
||||
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false, canUseAI: false });
|
||||
}
|
||||
},
|
||||
|
||||
@ -65,7 +69,7 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
||||
try {
|
||||
const license = await window.electronAPI?.activateLicense(key);
|
||||
if (!license) return false;
|
||||
set({ status: { tag: 'Licensed', license }, showDialog: false, canEdit: true });
|
||||
set({ status: { tag: 'Licensed', license }, showDialog: false, canEdit: true, canUseAI: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@ -77,9 +81,10 @@ export const useLicenseStore = create<LicenseState & LicenseActions>()(
|
||||
await window.electronAPI?.deactivateLicense();
|
||||
const s = await window.electronAPI?.getAppStatus();
|
||||
const canEdit = s?.tag === 'Licensed' || s?.tag === 'Trial';
|
||||
set({ status: s || { tag: 'Expired' }, isLoaded: true, canEdit });
|
||||
const canUseAI = s?.tag === 'Licensed';
|
||||
set({ status: s || { tag: 'Expired' }, isLoaded: true, canEdit, canUseAI });
|
||||
} catch {
|
||||
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false });
|
||||
set({ status: { tag: 'Expired' }, isLoaded: true, canEdit: false, canUseAI: false });
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user