feat: implement AI styling foundation and billing scope rework
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
|
||||
const navigateMock = vi.fn();
|
||||
@@ -20,6 +20,9 @@ const tMock = (
|
||||
|
||||
const downloadReceiptMock = vi.fn().mockResolvedValue(new Blob(['pdf'], { type: 'application/pdf' }));
|
||||
const triggerDownloadMock = vi.fn();
|
||||
const eventContext = {
|
||||
activeEvent: null as any,
|
||||
};
|
||||
|
||||
vi.mock('react-router-dom', () => ({
|
||||
useNavigate: () => navigateMock,
|
||||
@@ -119,6 +122,10 @@ vi.mock('../../constants', () => ({
|
||||
adminPath: (path: string) => path,
|
||||
}));
|
||||
|
||||
vi.mock('../../context/EventContext', () => ({
|
||||
useEventContext: () => eventContext,
|
||||
}));
|
||||
|
||||
vi.mock('../billingUsage', () => ({
|
||||
buildPackageUsageMetrics: () => [],
|
||||
formatPackageEventAllowance: () => '—',
|
||||
@@ -145,6 +152,7 @@ vi.mock('../invite-layout/export-utils', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../api', () => ({
|
||||
getEvent: vi.fn(),
|
||||
getTenantPackagesOverview: vi.fn().mockResolvedValue({ packages: [], activePackage: null }),
|
||||
getTenantBillingTransactions: vi.fn().mockResolvedValue({
|
||||
data: [
|
||||
@@ -170,7 +178,150 @@ import MobileBillingPage from '../BillingPage';
|
||||
import * as api from '../../api';
|
||||
|
||||
describe('MobileBillingPage', () => {
|
||||
beforeEach(() => {
|
||||
eventContext.activeEvent = null;
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(api.getTenantPackagesOverview).mockResolvedValue({ packages: [], activePackage: null });
|
||||
vi.mocked(api.getTenantBillingTransactions).mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
status: 'completed',
|
||||
amount: 49,
|
||||
currency: 'EUR',
|
||||
provider: 'paypal',
|
||||
provider_id: 'ORDER-1',
|
||||
package_name: 'Starter',
|
||||
purchased_at: '2024-01-01T00:00:00Z',
|
||||
receipt_url: '/api/v1/billing/transactions/1/receipt',
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
vi.mocked(api.getTenantAddonHistory).mockResolvedValue({ data: [] } as any);
|
||||
vi.mocked(api.getEvent).mockResolvedValue(null as any);
|
||||
});
|
||||
|
||||
it('shows current event scoped entitlements separately from tenant history', async () => {
|
||||
eventContext.activeEvent = {
|
||||
id: 99,
|
||||
slug: 'fruehlingsfest',
|
||||
name: { de: 'Frühlingsfest' },
|
||||
};
|
||||
|
||||
vi.mocked(api.getEvent).mockResolvedValueOnce({
|
||||
id: 99,
|
||||
slug: 'fruehlingsfest',
|
||||
name: { de: 'Frühlingsfest' },
|
||||
event_date: null,
|
||||
event_type_id: null,
|
||||
event_type: null,
|
||||
status: 'published',
|
||||
settings: {},
|
||||
package: {
|
||||
id: 201,
|
||||
name: 'Event Paket',
|
||||
price: 19,
|
||||
purchased_at: '2024-01-01T00:00:00Z',
|
||||
expires_at: '2024-02-01T00:00:00Z',
|
||||
branding_allowed: true,
|
||||
watermark_allowed: true,
|
||||
features: [],
|
||||
},
|
||||
capabilities: {
|
||||
ai_styling: false,
|
||||
ai_styling_granted_by: null,
|
||||
ai_styling_required_feature: null,
|
||||
ai_styling_addon_keys: [],
|
||||
ai_styling_event_enabled: true,
|
||||
ai_styling_allow_custom_prompt: true,
|
||||
ai_styling_allowed_style_keys: [],
|
||||
ai_styling_policy_message: null,
|
||||
},
|
||||
addons: [],
|
||||
limits: null,
|
||||
member_permissions: [],
|
||||
} as any);
|
||||
|
||||
vi.mocked(api.getTenantAddonHistory)
|
||||
.mockResolvedValueOnce({ data: [] })
|
||||
.mockResolvedValueOnce({
|
||||
data: [
|
||||
{
|
||||
id: 302,
|
||||
addon_key: 'extra_photos_100',
|
||||
label: '+100 photos',
|
||||
event: { id: 99, slug: 'fruehlingsfest', name: { de: 'Frühlingsfest' } },
|
||||
amount: 9,
|
||||
currency: 'EUR',
|
||||
status: 'completed',
|
||||
purchased_at: '2024-01-20T00:00:00Z',
|
||||
extra_photos: 100,
|
||||
extra_guests: 0,
|
||||
extra_gallery_days: 0,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
|
||||
render(<MobileBillingPage />);
|
||||
|
||||
await screen.findByText('Current event');
|
||||
expect(screen.getByText('Frühlingsfest')).toBeInTheDocument();
|
||||
expect(screen.getByText('Active for this event')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('+100 photos').length).toBeGreaterThan(0);
|
||||
expect(api.getTenantAddonHistory).toHaveBeenCalledWith({ eventId: 99, perPage: 6, page: 1 });
|
||||
});
|
||||
|
||||
it('marks add-ons purchased for another event in global history', async () => {
|
||||
eventContext.activeEvent = {
|
||||
id: 99,
|
||||
slug: 'fruehlingsfest',
|
||||
name: { de: 'Frühlingsfest' },
|
||||
};
|
||||
|
||||
vi.mocked(api.getEvent).mockResolvedValueOnce({
|
||||
id: 99,
|
||||
slug: 'fruehlingsfest',
|
||||
name: { de: 'Frühlingsfest' },
|
||||
event_date: null,
|
||||
event_type_id: null,
|
||||
event_type: null,
|
||||
status: 'published',
|
||||
settings: {},
|
||||
package: null,
|
||||
addons: [],
|
||||
limits: null,
|
||||
member_permissions: [],
|
||||
} as any);
|
||||
|
||||
vi.mocked(api.getTenantAddonHistory)
|
||||
.mockResolvedValueOnce({
|
||||
data: [
|
||||
{
|
||||
id: 400,
|
||||
addon_key: 'ai_styling_unlock',
|
||||
label: 'AI Magic Edits',
|
||||
event: { id: 77, slug: 'sommerfest', name: { de: 'Sommerfest' } },
|
||||
amount: 12,
|
||||
currency: 'EUR',
|
||||
status: 'completed',
|
||||
purchased_at: '2024-01-03T00:00:00Z',
|
||||
extra_photos: 0,
|
||||
extra_guests: 0,
|
||||
extra_gallery_days: 0,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} as any)
|
||||
.mockResolvedValueOnce({ data: [] } as any);
|
||||
|
||||
render(<MobileBillingPage />);
|
||||
|
||||
await screen.findByText('Purchased for another event');
|
||||
});
|
||||
|
||||
it('shows linked event information for a package', async () => {
|
||||
eventContext.activeEvent = null;
|
||||
vi.mocked(api.getTenantPackagesOverview).mockResolvedValueOnce({
|
||||
activePackage: {
|
||||
id: 11,
|
||||
@@ -207,6 +358,7 @@ describe('MobileBillingPage', () => {
|
||||
});
|
||||
|
||||
it('shows only recent package history and can expand the rest', async () => {
|
||||
eventContext.activeEvent = null;
|
||||
vi.mocked(api.getTenantPackagesOverview).mockResolvedValueOnce({
|
||||
activePackage: {
|
||||
id: 1,
|
||||
@@ -317,6 +469,7 @@ describe('MobileBillingPage', () => {
|
||||
});
|
||||
|
||||
it('downloads receipts via the API helper', async () => {
|
||||
eventContext.activeEvent = null;
|
||||
render(<MobileBillingPage />);
|
||||
|
||||
const receiptLink = await screen.findByText('Beleg');
|
||||
|
||||
Reference in New Issue
Block a user