teableio_teable/apps/nestjs-backend/test/plugin-chart.e2e-spec.ts
Uno ee1c14f5c3
refactor: move chart from plugin to nextjs-app (#1849)
* feat: add chart localization support in multiple languages

* feat: add plugin proxy configuration to Next.js app

* refactor:  improve plugin query  for dashboard and plugin panel

* refactor: move chart components and add ComponentPluginRender

* fix: update translation namespace for YAxisPositionEditor component

* fix: adjust column configuration in DashboardGrid for responsive layout

* refactor: remove baseQueryRoute and replace baseQuery function calls in e2e tests

* Revert "feat: add plugin proxy configuration to Next.js app"

This reverts commit 1e9c2dc1e2ba8d36b51290f53d6c882650238f4a.

* refactor: clean up global styles by removing unused CSS and updating imports

* refactor: remove chart components, API endpoints, and localization files in plugins

* fix: cell format in dashboard and plugin panel services

* fix: update query parameter in dashboard routing for consistency

* refactor: optimize useBaseQueryData with useMemo for performance improvements

* fix: can notsee the drop-down menu

* feat: auto active panel when plugin creation

* feat: enhance DashboardGrid responsiveness with breakpoint handling

* refactor: add PluginChartModule and clean up unused query endpoints

* refactor: remove unused IBaseQuery imports and clean up service dependencies

* refactor: remove unused pluginUserId and avatarPath from chart configuration

* refactor: using useMemo for pageParams and parentBridgeMethods

* refactor: update parameter names for plugin routes to maintain consistency

* test: add end-to-end tests for Plugin and Dashboard chart functionalities
2025-09-18 15:53:23 +08:00

252 lines
7.5 KiB
TypeScript

import type { INestApplication } from '@nestjs/common';
import { FieldType } from '@teable/core';
import type { IBaseQueryVo, ITableFullVo } from '@teable/openapi';
import {
createPluginPanel,
createDashboard,
deletePluginPanel,
getPluginPanelInstallPluginQuery,
getPluginPanelPlugin,
installPluginPanel,
pluginPanelPluginGetVoSchema,
updateDashboardPluginStorage,
updatePluginPanelStorage,
baseQuerySchemaVo,
urlBuilder,
GET_PLUGIN_PANEL_INSTALL_PLUGIN_QUERY,
deleteDashboard,
installPlugin,
getDashboardInstallPlugin,
getDashboardInstallPluginQuery,
GET_DASHBOARD_INSTALL_PLUGIN_QUERY,
getDashboardInstallPluginVoSchema,
} from '@teable/openapi';
import { createAnonymousUserAxios } from './utils/axios-instance/anonymous-user';
import { createTable, initApp, permanentDeleteTable } from './utils/init-app';
describe('PluginController', () => {
let app: INestApplication;
let anonymousUser: ReturnType<typeof createAnonymousUserAxios>;
beforeAll(async () => {
const appCtx = await initApp();
app = appCtx.app;
anonymousUser = createAnonymousUserAxios(appCtx.appUrl);
});
afterAll(async () => {
await app.close();
});
describe('Plugin Chart', () => {
let pluginPanelId: string;
let table: ITableFullVo;
const baseId = globalThis.testConfig.baseId;
beforeEach(async () => {
table = await createTable(baseId, {
fields: [
{
name: 'name',
type: FieldType.SingleLineText,
},
{
name: 'age',
type: FieldType.Number,
},
],
records: [
{
fields: {
name: 'Alice',
age: 20,
},
},
{
fields: {
name: 'Bob',
age: 30,
},
},
{
fields: {
name: 'Charlie',
age: 40,
},
},
],
});
});
afterEach(async () => {
await deletePluginPanel(table.id, pluginPanelId);
await permanentDeleteTable(baseId, table.id);
});
async function preparePluginPanel(table: ITableFullVo) {
const pluginPanelRes = await createPluginPanel(table.id, {
name: 'plugin panel',
});
pluginPanelId = pluginPanelRes.data.id;
const pluginId = 'plgchart';
const installRes = await installPluginPanel(table.id, pluginPanelId, {
name: 'plugin',
pluginId,
});
const pluginInstallId = installRes.data.pluginInstallId;
const textField = table.fields.find((field) => field.type === FieldType.SingleLineText)!;
const numberField = table.fields.find((field) => field.type === FieldType.Number)!;
const res = await getPluginPanelPlugin(table.id, pluginPanelId, pluginInstallId);
expect(res.status).toBe(200);
expect(pluginPanelPluginGetVoSchema.strict().safeParse(res.data).success).toBe(true);
expect(res.data.pluginId).toBe(pluginId);
await updatePluginPanelStorage(table.id, pluginPanelId, pluginInstallId, {
storage: {
config: {
type: 'bar',
xAxis: [{ column: textField.name, display: { type: 'bar', position: 'auto' } }],
yAxis: [{ column: numberField.name, display: { type: 'bar', position: 'auto' } }],
},
query: {
from: table.id,
select: [
{ column: textField.id, alias: textField.name, type: 'field' },
{ column: numberField.id, alias: numberField.name, type: 'field' },
],
},
},
});
return { pluginPanelId, pluginId, pluginInstallId };
}
it('api/plugin/chart/:pluginInstallId/plugin-panel/:positionId/query (GET)', async () => {
const { pluginPanelId, pluginInstallId } = await preparePluginPanel(table);
const queryRes = await getPluginPanelInstallPluginQuery(pluginInstallId, pluginPanelId, {
tableId: table.id,
});
expect(queryRes.status).toBe(200);
expect(baseQuerySchemaVo.strict().safeParse(queryRes.data).success).toBe(true);
await expect(
anonymousUser.get<IBaseQueryVo>(
urlBuilder(GET_PLUGIN_PANEL_INSTALL_PLUGIN_QUERY, {
pluginInstallId: pluginInstallId,
positionId: pluginPanelId,
}),
{
params: { tableId: table.id },
}
)
).rejects.toThrow();
});
});
describe('Dashboard Chart', () => {
let dashboardId: string;
let table: ITableFullVo;
const baseId = globalThis.testConfig.baseId;
beforeEach(async () => {
table = await createTable(baseId, {
fields: [
{
name: 'name',
type: FieldType.SingleLineText,
},
{
name: 'age',
type: FieldType.Number,
},
],
records: [
{
fields: {
name: 'Alice',
age: 20,
},
},
{
fields: {
name: 'Bob',
age: 30,
},
},
{
fields: {
name: 'Charlie',
age: 40,
},
},
],
});
});
afterEach(async () => {
await deleteDashboard(baseId, dashboardId);
await permanentDeleteTable(baseId, table.id);
});
async function prepareDashboard(table: ITableFullVo) {
const dashboardRes = await createDashboard(baseId, {
name: 'dashboard',
});
dashboardId = dashboardRes.data.id;
const pluginId = 'plgchart';
const installRes = await installPlugin(baseId, dashboardId, {
name: 'plugin',
pluginId,
});
const pluginInstallId = installRes.data.pluginInstallId;
const textField = table.fields.find((field) => field.type === FieldType.SingleLineText)!;
const numberField = table.fields.find((field) => field.type === FieldType.Number)!;
const res = await getDashboardInstallPlugin(baseId, dashboardId, pluginInstallId);
expect(res.status).toBe(200);
expect(getDashboardInstallPluginVoSchema.strict().safeParse(res.data).success).toBe(true);
expect(res.data.pluginId).toBe(pluginId);
await updateDashboardPluginStorage(baseId, dashboardId, pluginInstallId, {
config: {
type: 'bar',
xAxis: [{ column: textField.name, display: { type: 'bar', position: 'auto' } }],
yAxis: [{ column: numberField.name, display: { type: 'bar', position: 'auto' } }],
},
query: {
from: table.id,
select: [
{ column: textField.id, alias: textField.name, type: 'field' },
{ column: numberField.id, alias: numberField.name, type: 'field' },
],
},
});
return { dashboardId, pluginId, pluginInstallId };
}
it('api/plugin/chart/:pluginInstallId/dashboard/:positionId/query (GET)', async () => {
const { pluginInstallId, dashboardId } = await prepareDashboard(table);
const queryRes = await getDashboardInstallPluginQuery(pluginInstallId, dashboardId, {
baseId,
});
expect(queryRes.status).toBe(200);
expect(baseQuerySchemaVo.strict().safeParse(queryRes.data).success).toBe(true);
await expect(
anonymousUser.get<IBaseQueryVo>(
urlBuilder(GET_DASHBOARD_INSTALL_PLUGIN_QUERY, {
pluginInstallId,
positionId: dashboardId,
}),
{
params: { baseId },
}
)
).rejects.toThrow();
});
});
});