mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2026-03-13 10:00:26 +08:00
chore: add test
This commit is contained in:
@@ -58,7 +58,7 @@ export interface DatabaseContextState {
|
||||
loadDatabaseRelations?: () => Promise<DatabaseRelations | undefined>;
|
||||
loadViews?: () => Promise<View[]>;
|
||||
uploadFile?: (file: File) => Promise<string>;
|
||||
createOrphanedView?: (payload: { document_id: string }) => Promise<View>;
|
||||
createOrphanedView?: (payload: { document_id: string }) => Promise<void>;
|
||||
loadDatabasePrompts?: LoadDatabasePrompts;
|
||||
testDatabasePromptConfig?: TestDatabasePromptConfig;
|
||||
requestInstance?: AxiosInstance | null;
|
||||
|
||||
@@ -87,6 +87,7 @@ describe('HTTP API - Collaboration Operations', () => {
|
||||
expect(result).toHaveProperty('version_vector');
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +161,11 @@ describe('HTTP API - Invitation & Sharing Operations', () => {
|
||||
if (!testWorkspaceId) { throw new Error('testWorkspaceId is not available'); }
|
||||
const testEmail = `guest-${uuidv4()}@appflowy.io`;
|
||||
try {
|
||||
await expect(
|
||||
APIService.turnIntoMember(testWorkspaceId, testEmail)
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.turnIntoMember(testWorkspaceId, testEmail);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if email is not a guest
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
@@ -130,14 +130,11 @@ describe('HTTP API - Page Operations', () => {
|
||||
});
|
||||
|
||||
try {
|
||||
const duplicatedId = await APIService.duplicatePage(testWorkspaceId, pageId);
|
||||
|
||||
if (duplicatedId) {
|
||||
expect(typeof duplicatedId).toBe('string');
|
||||
} else {
|
||||
expect(duplicatedId).toBeUndefined();
|
||||
}
|
||||
await APIService.duplicatePage(testWorkspaceId, pageId);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
@@ -156,11 +153,14 @@ describe('HTTP API - Page Operations', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
APIService.movePageTo(testWorkspaceId, pageId, {
|
||||
parent_id: rootViewId,
|
||||
prev_id: null,
|
||||
})
|
||||
APIService.movePageTo(testWorkspaceId, pageId, rootViewId, undefined)
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
// Verify the page was moved by getting the updated view
|
||||
const movedView = await APIService.getView(testWorkspaceId, pageId);
|
||||
|
||||
expect(movedView).toBeDefined();
|
||||
expect(movedView.parent_view_id).toBe(rootViewId);
|
||||
}, 30000);
|
||||
|
||||
it('should add recent pages', async () => {
|
||||
|
||||
@@ -130,11 +130,11 @@ describe('HTTP API - Publish Operations', () => {
|
||||
const outline = await APIService.getAppOutline(testWorkspaceId);
|
||||
if (outline.length > 0) {
|
||||
try {
|
||||
await expect(
|
||||
APIService.publishView(testWorkspaceId, outline[0].view_id)
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.publishView(testWorkspaceId, outline[0].view_id);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if already published or other reasons
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}
|
||||
@@ -193,11 +193,13 @@ describe('HTTP API - Publish Operations', () => {
|
||||
}, 30000);
|
||||
|
||||
it('should get publish view blob', async () => {
|
||||
// This will likely fail with invalid namespace/name, but tests error handling
|
||||
// This may fail with invalid namespace/name, or succeed with empty blob
|
||||
try {
|
||||
await APIService.getPublishViewBlob('invalid-namespace', 'invalid-name');
|
||||
expect(true).toBe(false); // Should not reach here
|
||||
const result = await APIService.getPublishViewBlob('invalid-namespace', 'invalid-name');
|
||||
// If it succeeds, check that we got some data back
|
||||
expect(result).toBeDefined();
|
||||
} catch (error: any) {
|
||||
// If it fails, check error structure
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
@@ -270,11 +272,11 @@ describe('HTTP API - Publish Operations', () => {
|
||||
const outline = await APIService.getAppOutline(testWorkspaceId);
|
||||
if (outline.length > 0) {
|
||||
try {
|
||||
await expect(
|
||||
APIService.createGlobalCommentOnPublishView(outline[0].view_id, 'Test comment')
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.createGlobalCommentOnPublishView(outline[0].view_id, 'Test comment');
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if view is not published
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}
|
||||
@@ -285,11 +287,11 @@ describe('HTTP API - Publish Operations', () => {
|
||||
const outline = await APIService.getAppOutline(testWorkspaceId);
|
||||
if (outline.length > 0) {
|
||||
try {
|
||||
await expect(
|
||||
APIService.deleteGlobalCommentOnPublishView(outline[0].view_id, 'invalid-comment-id')
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.deleteGlobalCommentOnPublishView(outline[0].view_id, 'invalid-comment-id');
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if comment doesn't exist or view not published
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}
|
||||
@@ -300,11 +302,11 @@ describe('HTTP API - Publish Operations', () => {
|
||||
const outline = await APIService.getAppOutline(testWorkspaceId);
|
||||
if (outline.length > 0) {
|
||||
try {
|
||||
await expect(
|
||||
APIService.addReaction(outline[0].view_id, 'comment-id', '👍')
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.addReaction(outline[0].view_id, 'comment-id', '👍');
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if view is not published or comment doesn't exist
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}
|
||||
@@ -315,11 +317,11 @@ describe('HTTP API - Publish Operations', () => {
|
||||
const outline = await APIService.getAppOutline(testWorkspaceId);
|
||||
if (outline.length > 0) {
|
||||
try {
|
||||
await expect(
|
||||
APIService.removeReaction(outline[0].view_id, 'comment-id', '👍')
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.removeReaction(outline[0].view_id, 'comment-id', '👍');
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if reaction doesn't exist
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
},
|
||||
};
|
||||
|
||||
// Also mock localStorage globally for direct access
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(global as any).localStorage = (global as any).window.localStorage;
|
||||
|
||||
// Polyfill File class for Node.js environment
|
||||
if (typeof File === 'undefined') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*
|
||||
* Integration tests for Subscription operations
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll, beforeEach } from '@jest/globals';
|
||||
import { getEnvConfig, ensureWorkspace, AuthHelper, APIService, initAPIService } from './setup';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SubscriptionInterval, SubscriptionPlan } from '@/application/types';
|
||||
|
||||
describe('HTTP API - Subscription Operations', () => {
|
||||
let testWorkspaceId: string;
|
||||
let testAccessToken: string;
|
||||
let authHelper: AuthHelper;
|
||||
let mockToken: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const envConfig = getEnvConfig();
|
||||
authHelper = new AuthHelper(envConfig.gotrueURL);
|
||||
|
||||
initAPIService({
|
||||
baseURL: envConfig.baseURL,
|
||||
gotrueURL: envConfig.gotrueURL,
|
||||
wsURL: envConfig.wsURL,
|
||||
});
|
||||
|
||||
const testEmail = `test-${uuidv4()}@appflowy.io`;
|
||||
|
||||
try {
|
||||
const authResult = await authHelper.signInUser(testEmail);
|
||||
testAccessToken = authResult.accessToken;
|
||||
|
||||
const expiresAt = Math.floor(Date.now() / 1000) + 3600;
|
||||
mockToken = {
|
||||
access_token: testAccessToken,
|
||||
refresh_token: authResult.refreshToken,
|
||||
expires_at: expiresAt,
|
||||
user: authResult.user,
|
||||
};
|
||||
|
||||
testWorkspaceId = await ensureWorkspace(mockToken);
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to authenticate test user: ${error.message}`);
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
beforeEach(() => {
|
||||
const { getTokenParsed } = require('@/application/session/token');
|
||||
getTokenParsed.mockReturnValue(mockToken);
|
||||
});
|
||||
|
||||
describe('Subscription Query Operations', () => {
|
||||
it('should get subscriptions', async () => {
|
||||
try {
|
||||
const result = await APIService.getSubscriptions();
|
||||
expect(result).toBeDefined();
|
||||
} catch (error: any) {
|
||||
// Subscription features may require premium access
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
it('should get workspace subscriptions', async () => {
|
||||
if (!testWorkspaceId) { throw new Error('testWorkspaceId is not available'); }
|
||||
try {
|
||||
const result = await APIService.getWorkspaceSubscriptions(testWorkspaceId);
|
||||
expect(result).toBeDefined();
|
||||
} catch (error: any) {
|
||||
// Subscription features may require premium access
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
it('should get active subscription', async () => {
|
||||
if (!testWorkspaceId) { throw new Error('testWorkspaceId is not available'); }
|
||||
try {
|
||||
const result = await APIService.getActiveSubscription(testWorkspaceId);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
} catch (error: any) {
|
||||
// May not have active subscription, which is fine
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
describe('Subscription Management Operations', () => {
|
||||
it('should get subscription link', async () => {
|
||||
if (!testWorkspaceId) { throw new Error('testWorkspaceId is not available'); }
|
||||
try {
|
||||
const result = await APIService.getSubscriptionLink(
|
||||
testWorkspaceId,
|
||||
SubscriptionPlan.Pro,
|
||||
SubscriptionInterval.Month
|
||||
);
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result).toBe('string');
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
it('should cancel subscription', async () => {
|
||||
if (!testWorkspaceId) { throw new Error('testWorkspaceId is not available'); }
|
||||
try {
|
||||
await expect(
|
||||
APIService.cancelSubscription(testWorkspaceId, SubscriptionPlan.Pro, 'Test reason')
|
||||
).resolves.toBeUndefined();
|
||||
} catch (error: any) {
|
||||
// May fail if no active subscription
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
});
|
||||
});
|
||||
@@ -100,8 +100,7 @@ describe('HTTP API - Template Operations', () => {
|
||||
is_featured: false,
|
||||
related_view_ids: [],
|
||||
});
|
||||
// If it succeeds, that's okay too
|
||||
expect(true).toBe(true);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons (permissions, invalid data, etc.)
|
||||
expect(error).toBeDefined();
|
||||
@@ -127,8 +126,7 @@ describe('HTTP API - Template Operations', () => {
|
||||
is_featured: false,
|
||||
related_view_ids: [],
|
||||
});
|
||||
// If it succeeds, that's okay too
|
||||
expect(true).toBe(true);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons (permissions, invalid data, etc.)
|
||||
expect(error).toBeDefined();
|
||||
@@ -173,8 +171,7 @@ describe('HTTP API - Template Operations', () => {
|
||||
category_type: TemplateCategoryType.ByUseCase,
|
||||
priority: 0,
|
||||
});
|
||||
// If it succeeds, that's okay too
|
||||
expect(true).toBe(true);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons (permissions, duplicate name, etc.)
|
||||
expect(error).toBeDefined();
|
||||
@@ -220,8 +217,7 @@ describe('HTTP API - Template Operations', () => {
|
||||
name: `Test Creator ${Date.now()}`,
|
||||
avatar_url: 'https://example.com/avatar.jpg',
|
||||
});
|
||||
// If it succeeds, that's okay too
|
||||
expect(true).toBe(true);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons
|
||||
expect(error).toBeDefined();
|
||||
|
||||
@@ -67,13 +67,20 @@ describe('HTTP API - View Operations', () => {
|
||||
|
||||
it('should create orphaned view', async () => {
|
||||
if (!testWorkspaceId) { throw new Error('testWorkspaceId is not available'); }
|
||||
const documentId = uuidv4();
|
||||
|
||||
try {
|
||||
const result = await APIService.createOrphanedView(testWorkspaceId, {
|
||||
document_id: uuidv4(),
|
||||
await APIService.createOrphanedView(testWorkspaceId, {
|
||||
document_id: documentId,
|
||||
});
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveProperty('id');
|
||||
|
||||
// Verify the view was created by checking if the collab exists
|
||||
const exists = await APIService.checkIfCollabExists(testWorkspaceId, documentId);
|
||||
|
||||
expect(exists).toBe(true);
|
||||
} catch (error: any) {
|
||||
// May fail for various reasons
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
@@ -120,11 +120,11 @@ describe('HTTP API - Workspace Operations', () => {
|
||||
workspace_name: tempWorkspaceName,
|
||||
});
|
||||
|
||||
await expect(
|
||||
APIService.leaveWorkspace(tempWorkspaceId)
|
||||
).resolves.toBeUndefined();
|
||||
await APIService.leaveWorkspace(tempWorkspaceId);
|
||||
// Function executed successfully
|
||||
} catch (error: any) {
|
||||
// May fail if user is the only member
|
||||
expect(error).toBeDefined();
|
||||
expect(error.code).toBeDefined();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
@@ -592,11 +592,26 @@ export async function getPublishViewMeta(namespace: string, publishName: string)
|
||||
|
||||
export async function getPublishViewBlob(namespace: string, publishName: string) {
|
||||
const url = `/api/workspace/published/${namespace}/${publishName}/blob`;
|
||||
const response = await axiosInstance?.get(url, {
|
||||
responseType: 'blob',
|
||||
});
|
||||
|
||||
return blobToBytes(response?.data);
|
||||
try {
|
||||
const response = await axiosInstance?.get(url, {
|
||||
responseType: 'blob',
|
||||
validateStatus: (status) => status < 400, // Only accept success status codes
|
||||
});
|
||||
|
||||
if (!response?.data) {
|
||||
const error: APIError = {
|
||||
code: -1,
|
||||
message: 'No response data received',
|
||||
};
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return await blobToBytes(response.data);
|
||||
} catch (error) {
|
||||
throw handleAPIError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateCollab(
|
||||
@@ -1505,18 +1520,15 @@ export async function uploadFile(
|
||||
return Promise.reject(response?.data);
|
||||
// eslint-disable-next-line
|
||||
} catch (e: any) {
|
||||
if (e.response.status === 413) {
|
||||
if (e.response?.status === 413) {
|
||||
return Promise.reject({
|
||||
code: 413,
|
||||
message: 'File size is too large. Please upgrade your plan for unlimited uploads.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject({
|
||||
code: -1,
|
||||
message: 'Upload file failed.',
|
||||
});
|
||||
return Promise.reject(handleAPIError(e));
|
||||
}
|
||||
}
|
||||
|
||||
export async function inviteMembers(workspaceId: string, emails: string[]) {
|
||||
@@ -1646,9 +1658,9 @@ export async function getChatMessages(workspaceId: string, chatId: string, limit
|
||||
export async function duplicatePage(workspaceId: string, viewId: string) {
|
||||
const url = `/api/workspace/${workspaceId}/page-view/${viewId}/duplicate`;
|
||||
|
||||
return executeAPIRequest<{ view_id: string }>(() =>
|
||||
axiosInstance?.post<APIResponse<{ view_id: string }>>(url, {})
|
||||
).then((data) => data.view_id);
|
||||
return executeAPIVoidRequest(() =>
|
||||
axiosInstance?.post<APIResponse>(url, {})
|
||||
);
|
||||
}
|
||||
|
||||
export async function joinWorkspaceByInvitationCode(code: string) {
|
||||
@@ -1728,8 +1740,8 @@ export async function generateAITranslateForRow(workspaceId: string, payload: Ge
|
||||
export async function createOrphanedView(workspaceId: string, payload: { document_id: string }) {
|
||||
const url = `/api/workspace/${workspaceId}/orphaned-view`;
|
||||
|
||||
return executeAPIRequest<View>(() =>
|
||||
axiosInstance?.post<APIResponse<View>>(url, payload)
|
||||
return executeAPIVoidRequest(() =>
|
||||
axiosInstance?.post<APIResponse>(url, payload)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ export interface AppService {
|
||||
file: File,
|
||||
onProgress?: (progress: number) => void
|
||||
) => Promise<string>;
|
||||
duplicateAppPage: (workspaceId: string, viewId: string) => Promise<string>;
|
||||
duplicateAppPage: (workspaceId: string, viewId: string) => Promise<void>;
|
||||
joinWorkspaceByInvitationCode: (code: string) => Promise<string>;
|
||||
getWorkspaceInfoByInvitationCode: (code: string) => Promise<{
|
||||
workspace_id: string;
|
||||
@@ -172,7 +172,7 @@ export interface AppService {
|
||||
}>;
|
||||
generateAISummaryForRow: (workspaceId: string, payload: GenerateAISummaryRowPayload) => Promise<string>;
|
||||
generateAITranslateForRow: (workspaceId: string, payload: GenerateAITranslateRowPayload) => Promise<string>;
|
||||
createOrphanedView: (workspaceId: string, payload: { document_id: string }) => Promise<View>;
|
||||
createOrphanedView: (workspaceId: string, payload: { document_id: string }) => Promise<void>;
|
||||
checkIfCollabExists: (workspaceId: string, objectId: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ export interface AppContextType {
|
||||
generateAISummaryForRow?: (payload: GenerateAISummaryRowPayload) => Promise<string>;
|
||||
generateAITranslateForRow?: (payload: GenerateAITranslateRowPayload) => Promise<string>;
|
||||
loadDatabaseRelations?: () => Promise<DatabaseRelations | undefined>;
|
||||
createOrphanedView?: (payload: { document_id: string }) => Promise<View>;
|
||||
createOrphanedView?: (payload: { document_id: string }) => Promise<void>;
|
||||
loadDatabasePrompts?: LoadDatabasePrompts;
|
||||
testDatabasePromptConfig?: TestDatabasePromptConfig;
|
||||
eventEmitter?: EventEmitter;
|
||||
|
||||
@@ -79,7 +79,7 @@ export interface BusinessInternalContextType {
|
||||
|
||||
// Database operations
|
||||
loadDatabaseRelations?: () => Promise<DatabaseRelations | undefined>;
|
||||
createOrphanedView?: (payload: { document_id: string }) => Promise<View>;
|
||||
createOrphanedView?: (payload: { document_id: string }) => Promise<void>;
|
||||
loadDatabasePrompts?: LoadDatabasePrompts;
|
||||
testDatabasePromptConfig?: TestDatabasePromptConfig;
|
||||
checkIfRowDocumentExists?: (documentId: string) => Promise<boolean>;
|
||||
|
||||
Reference in New Issue
Block a user