YANG QIA 3aa65cb30c
feat: data visualization (#2160)
* feat(charts-v2): init

* chore(charts-v2): init chart renderer

* feat(chart-v2): add chart grid and initializer

* feat(chart-v2): improve ui

* feat(chart-v2): ui

* feat(charts-v2): query sort ui

* feat(charts-v2): field select component

* feat(charts-v2): improve ui && add query action

* feat(charts-v2): imporve ui, work in progress

* fix(charts-v2): chart renderer request api twice

* feat(charts-v2): add dimension formatter

* feat(charts-v2): filter, sort, limit

* feat(charts-v2): sql mode ui

* feat(charts-v2): support duplicate & sql mode

* fix(charts-v2): wrong defaultValue of json config

* feat(charts-v2): transformer ui

* feat(charts-v2): transformer

* chore(charts-v2): rename transfromer to transform

* feat(charts-v2): support cache

* feat(charts-v2): add acl provider

* chore(charts-v2): hide sql mode

* refactor(charts-v2): add renderer provider

* feat: collection permission check

* feat(charts-v2): add antd statistic

* test(charts-v2): backend

* chore: improve code

* test(charts-v2): add test

* chore: add Chinese translation

* fix(charts-v2): locale switch bug

* chore: add dependency

* feat(charts-v2): init chart config from query

* feat: change layout

* test: fix frontend test

* feat: improve auto infer

* fix: ui issues

* chore: translation

* fix: sql error

* fix: some issues

* feat: support table

* fix: bug

* chore: improve code and fix query

* feat: add config reference

* chore: add translation

* fix: process data due to pg issue

* test: fix parseBuilder

* chore: upgrade formily to 2.2.25

* fix: some issues and import style

* fix: bug when query with sort

* feat: parse enum data

* fix: yarn.lock

* fix: type error

* fix: infer bug and frontend test

* test: fix frontend

* fix: test

* feat: improve preview

* chore: downgrade formily

* feat: support associations, draft, in testing

* fix: typo

* test: frontend & backend

* fix: infer bug

* feat: measure selection of statistics

* fix: bug of group by alias

* fix: some issues

* fix: order issues

* fix: yarn.lock

* chore: fix filter include & 'data-visualization'

* style: improve style

* docs: add readme

* chore: add translation

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
2023-06-30 20:49:44 +08:00

262 lines
6.5 KiB
TypeScript

import * as client from '@nocobase/client';
import { renderHook } from '@testing-library/react';
import { vi } from 'vitest';
import formatters from '../block/formatters';
import transformers from '../block/transformers';
import {
useChartFields,
useFieldsWithAssociation,
useFieldTransformer,
useFieldTypes,
useFormatters,
useOrderFieldsOptions,
useTransformers,
} from '../hooks';
describe('hooks', () => {
beforeEach(() => {
vi.spyOn(client, 'useCollectionManager').mockReturnValue({
getCollectionFields: (name: string) =>
({
orders: [
{
interface: 'string',
name: 'name',
uiSchema: {
title: '{{t("Name")}}',
},
type: 'string',
},
{
interface: 'number',
name: 'price',
uiSchema: {
title: '{{t("Price")}}',
},
type: 'double',
},
{
interface: 'createdAt',
name: 'createdAt',
uiSchema: {
title: '{{t("Created At")}}',
},
type: 'date',
},
{
interface: 'm2o',
name: 'user',
uiSchema: {
title: '{{t("User")}}',
},
target: 'users',
type: 'belongsTo',
},
],
users: [
{
interface: 'string',
name: 'name',
uiSchema: {
title: '{{t("Name")}}',
},
type: 'string',
},
],
}[name]),
} as any);
});
afterEach(() => {
vi.restoreAllMocks();
});
test('useFieldsWithAssociation', () => {
const { result } = renderHook(() => useFieldsWithAssociation('orders'));
expect(result.current).toMatchObject([
{
key: 'name',
label: 'Name',
value: 'name',
},
{
key: 'price',
label: 'Price',
value: 'price',
},
{
key: 'createdAt',
label: 'Created At',
value: 'createdAt',
},
{
key: 'user',
label: 'User',
value: 'user',
target: 'users',
targetFields: [
{
key: 'user.name',
label: 'User / Name',
value: 'user.name',
},
],
},
]);
});
test('useChartFields', () => {
const fields = renderHook(() => useFieldsWithAssociation('orders')).result.current;
const { result } = renderHook(() => useChartFields(fields));
const func = result.current;
const field = {
query: () => ({
get: () => ({
measures: [
{
field: ['price'],
alias: 'Price Alias',
},
],
dimensions: [
{
field: ['user', 'name'],
},
],
}),
}),
dataSource: [],
};
func(field);
expect(field.dataSource).toMatchObject([
{
key: 'Price Alias',
label: 'Price Alias',
value: 'Price Alias',
},
{
key: 'user.name',
label: 'User / Name',
value: 'user.name',
},
]);
});
test('useFormatters', () => {
const fields = renderHook(() => useFieldsWithAssociation('orders')).result.current;
const { result } = renderHook(() => useFormatters(fields));
const func = result.current;
const field = {
query: () => ({
get: () => 'createdAt',
}),
dataSource: [],
};
func(field);
expect(field.dataSource).toEqual(formatters.datetime);
});
test('useFieldTypes', () => {
const fields = renderHook(() => useFieldsWithAssociation('orders')).result.current;
const { result } = renderHook(() => useFieldTypes(fields));
const func = result.current;
let state1 = {};
let state2 = {};
const field = {
dataSource: [],
state: {},
};
const query = (path: string, val: string) => ({
get: () => {
if (path === 'query') {
return { measures: [{ field: ['price'] }, { field: ['name'] }] };
}
return val;
},
});
const field1 = {
query: (path: string) => query(path, 'price'),
setState: (state) => (state1 = state),
...field,
};
const field2 = {
query: (path: string) => query(path, 'name'),
setState: (state) => (state2 = state),
...field,
};
func(field1);
func(field2);
expect(field1.dataSource.map((item) => item.value)).toEqual(Object.keys(transformers));
expect(state1).toEqual({ value: 'number', disabled: true });
expect(state2).toEqual({ value: null, disabled: false });
});
test('useTransformers', () => {
const field = {
query: () => ({
get: () => 'datetime',
}),
dataSource: [],
};
renderHook(() => useTransformers(field));
expect(field.dataSource.map((item) => item.value)).toEqual(Object.keys(transformers['datetime']));
});
test('useFieldTransformers', () => {
const { result } = renderHook(() =>
useFieldTransformer([
{
field: '1',
type: 'datetime',
format: 'YYYY',
},
{
field: '2',
type: 'number',
format: 'YYYY',
},
]),
);
expect(result.current['1']).toBeDefined();
expect(result.current['2']).toBeUndefined();
});
test('useOrderFieldsOptions', () => {
const fields = renderHook(() => useFieldsWithAssociation('orders')).result.current;
const { result } = renderHook(() => useOrderFieldsOptions([], fields));
const func = result.current;
const field1 = {
query: () => ({
get: () => ({
measures: [{ field: ['price'] }],
}),
}),
dataSource: [],
componentProps: {
fieldNames: {},
},
};
const field2 = {
query: () => ({
get: () => ({
measures: [{ field: ['price'], aggregation: 'sum' }],
}),
}),
componentProps: {
fieldNames: {},
},
dataSource: [],
};
func(field1);
func(field2);
expect(field1.dataSource).toEqual([]);
expect(field1.componentProps.fieldNames).toEqual({
label: 'title',
value: 'name',
children: 'children',
});
expect(field2.dataSource).toMatchObject([{ key: 'price', value: 'price', label: 'Price' }]);
expect(field2.componentProps.fieldNames).toEqual({});
});
});