test: client ui test (#2736)
* test: add menu group test * test: add menu link test * test: insert befort page test * test(e2e): better locators for designer buttons * fix: make test passing * test: esit menu title * test: move menu * test: move menu * test: move menu * test: menu insert page before * test: menu insert page before * test: menu test * test: menu test * test: menu test * test: menu test * test: menu test * test: menu test * test: insert page after * test: insert page after * test: create new page * test: menu test refactor * test: page test * test: page header * test: page title * test: edit page title * refactor: remove DesignerControl * test: page test * test: page test * test: page tab test * test: page tab move test * test: page tab move test * test: block test * chore: better locators * test: add block test * test: block test * test: action & field test * fix: should not disable add-menu-item * chore: better test id for block * chore: optimize Action * chore: remove role in BlockItem * test: block test * test: edit block title * test: edit block title * test: plugin manager ui test * test: plugin manager remove ui test * test: plugin manager remove ui test * test: plugin manager remove ui test * test: plugin manager enable ui test * feat: improve locators * test: menu test * test: menu test * test: menu test * test: page test * test: page test * test: block test * test: block test * test: block test * test: field test * test: field test * test: field test * test: field test * test: field test * test: field test * chore: menu & add block * test: action test * chore: initializer * chore: testid -> aria label * chore: tabs * test: action test * test: action test * test: action test * chore: designers * refactor: optimize schemaInitializer * refactor: rename * chore: add collectionName * chore: block item * chore: action * fix: avoid crashting * chore(e2e): add __E2E__ * chore: all dialog * test: action test * test: action test * test: field test * test: field test * test: field test * test: field test * test: page test * chore: add aria-label for block menu * Revert "chore: add aria-label for block menu" This reverts commit 6a840ef816ee1095484dc268b5dfa1bbe6cd8cbe. * test: action test * test: page test * chore: optimize aria-label of Action * test: pm test * chore: schema-initializer * test: pm test * chore(e2e): increase timeout * chore: schema settings * chore: optimize table * chore: workflow * chore: plugin manager * chore: collection manager and workflow * chore: details of workflow * chore: remove testid of Select * test: fix unit-tests * test: fix unit-tests * test: menu test * test: page test * test(e2e): passing tests * test: page test * test: block test * test: field test * test: fix unit test * chore: should use hover * test: field test * test: passing tests * chore: passing tests * test: action test * chore: fix CI * chore: fix CI * test: pm test * chore: increase timeout in CI * test: blcok test * revert: plugin card * test: pm test * test: pm test * chore: remove testid for Modal * refactor: rename testid * fix: __E2E__ should be true in CI * fix(buildPlugin): __E2E__ should be true in CI * fix: __E2E__ * Revert "fix: __E2E__" This reverts commit c899a33761fbfab94283f6bf41eae197f889068b. * fix: __E2E__ * fix: passing tests * test: action test optimize * test: menu page pm test * test: menu page pm test --------- Co-authored-by: Rain <958414905@qq.com> Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
9ec2b50990
commit
9a730a8fec
@ -8,7 +8,6 @@ import supertest, { SuperAgentTest } from 'supertest';
|
||||
import db2resource from '../../../server/src/middlewares/db2resource';
|
||||
import { uid } from '@nocobase/utils';
|
||||
|
||||
|
||||
interface ActionParams {
|
||||
fields?: string[];
|
||||
filter?: any;
|
||||
@ -117,7 +116,8 @@ export class MockServer extends Koa {
|
||||
{
|
||||
get(target, method: string, receiver) {
|
||||
return (params: ActionParams = {}) => {
|
||||
let { filterByTk, values = {}, file, ...restParams } = params;
|
||||
let { filterByTk } = params;
|
||||
const { values = {}, file, ...restParams } = params;
|
||||
if (params.associatedIndex) {
|
||||
resourceOf = params.associatedIndex;
|
||||
}
|
||||
|
342
packages/core/client/src/__tests__/e2e/action.test.ts
Normal file
342
packages/core/client/src/__tests__/e2e/action.test.ts
Normal file
@ -0,0 +1,342 @@
|
||||
import { expect, test } from '@nocobase/test/client';
|
||||
const tablePageSchema = {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
'3r0yxum84w2': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
properties: {
|
||||
vduni5v1u2v: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
qwrauugqc1k: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
'92vs3ej14vo': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'TableBlockProvider',
|
||||
'x-acl-action': 'users:list',
|
||||
'x-decorator-props': {
|
||||
collection: 'users',
|
||||
resource: 'users',
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 20,
|
||||
},
|
||||
rowKey: 'id',
|
||||
showIndex: true,
|
||||
dragSort: false,
|
||||
disableTemplate: false,
|
||||
},
|
||||
'x-designer': 'TableBlockDesigner',
|
||||
'x-component': 'CardItem',
|
||||
'x-filter-targets': [],
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-initializer': 'TableActionInitializers',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 'var(--nb-spacing)',
|
||||
},
|
||||
},
|
||||
'x-uid': 'gzu2uanef12',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
vlkh4fiq98e: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'array',
|
||||
'x-initializer': 'TableColumnInitializers',
|
||||
'x-component': 'TableV2',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
useProps: '{{ useTableBlockProps }}',
|
||||
},
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Actions") }}',
|
||||
'x-action-column': 'actions',
|
||||
'x-decorator': 'TableV2.Column.ActionBar',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||
'x-initializer': 'TableActionColumnInitializers',
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
'x-uid': 'q44b0dv50za',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'i8tnis8ymcm',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'u60i2x1lo97',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': '6jb6jfh1770',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '9oykdej2ufq',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '8iolmzoelll',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '0dhkzj75zh7',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'n8gs3wg1sxp',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
};
|
||||
|
||||
test.describe('add action & remove action', () => {
|
||||
test('add action & remove action in block', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-table').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').click();
|
||||
//添加按钮
|
||||
await page.getByLabel('Enable actions-Filter').click();
|
||||
await page.getByLabel('Enable actions-Add new').click();
|
||||
await page.getByLabel('Enable actions-Delete').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
|
||||
await page.getByText('Enable actions').hover();
|
||||
await expect(page.getByLabel('action-Filter.Action-Filter-filter-users-table')).toBeVisible();
|
||||
await expect(page.getByLabel('action-Action-Add new-create-users-table')).toBeVisible();
|
||||
await expect(page.getByLabel('action-Action-Delete-destroy-users-table')).toBeVisible();
|
||||
await expect(page.getByLabel('Enable actions-Filter').getByRole('switch')).toBeChecked();
|
||||
await expect(page.getByLabel('Enable actions-Add new').getByRole('switch')).toBeChecked();
|
||||
await expect(page.getByLabel('Enable actions-Delete').getByRole('switch')).toBeChecked();
|
||||
//移除按钮
|
||||
await page.getByLabel('Enable actions-Filter').click();
|
||||
await page.getByLabel('Enable actions-Add new').click();
|
||||
await page.getByLabel('Enable actions-Delete').click();
|
||||
await expect(page.getByLabel('action-Action-Add new-create-users-table')).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByLabel('block-item-CardItem-users-table').getByRole('button', { name: 'Delete' }),
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByLabel('block-item-CardItem-users-table').getByLabel('Filter', { exact: true }),
|
||||
).not.toBeVisible();
|
||||
await expect(page.getByLabel('Enable actions-Filter').getByRole('switch')).not.toBeChecked();
|
||||
await expect(page.getByLabel('Enable actions-Add new').getByRole('switch')).not.toBeChecked();
|
||||
await expect(page.getByLabel('Enable actions-Delete').getByRole('switch')).not.toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('action drag in block', () => {
|
||||
test('drag th action orders', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-table').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').click();
|
||||
//添加按钮
|
||||
await page.getByLabel('Enable actions-Add new').click();
|
||||
await page.getByLabel('Enable actions-Delete').click();
|
||||
await page.getByLabel('Enable actions-Refresh').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
|
||||
await page.getByText('Enable actions').hover();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
|
||||
const addNewBtn = await page.getByLabel('action-Action-Add new-create-users-table');
|
||||
await addNewBtn.hover();
|
||||
const addNewDrag = await page.getByLabel('action-Action-Add new-create-users-table').getByLabel('designer-drag');
|
||||
await addNewDrag.hover();
|
||||
|
||||
const delBtn = await page.getByLabel('action-Action-Delete-destroy-users-table');
|
||||
await addNewDrag.dragTo(delBtn);
|
||||
const refreshBtn = await page.getByLabel('action-Action-Refresh-refresh-users-table');
|
||||
await addNewBtn.hover();
|
||||
await addNewBtn.getByLabel('designer-drag').dragTo(refreshBtn);
|
||||
|
||||
const addNew = await addNewBtn.boundingBox();
|
||||
const del = await delBtn.boundingBox();
|
||||
const refresh = await refreshBtn.boundingBox();
|
||||
const max = Math.max(addNew.x, refresh.x, del.x);
|
||||
//拖拽调整排序符合预期
|
||||
await expect(addNew.x).toBe(max);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('action display config', () => {
|
||||
test('editing action name,icon and color', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-table').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').click();
|
||||
//添加按钮
|
||||
await page.getByLabel('Enable actions-Add new').click();
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').hover();
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').getByLabel('designer-schema-settings').hover();
|
||||
//更新按钮图标、名称、样式
|
||||
await page.getByLabel('Edit button').click();
|
||||
await page.getByRole('textbox').fill('Add new1');
|
||||
await page.getByRole('button', { name: 'close', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Select icon', exact: true }).click();
|
||||
await page.getByLabel('user-add').click();
|
||||
await page.getByLabel('Danger red').check();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
await expect(
|
||||
await page.getByLabel('block-item-CardItem-users-table').locator('.nb-action-bar').getByLabel('user-add'),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByLabel('block-item-CardItem-users-table').locator('.nb-action-bar').locator('.ant-btn-dangerous'),
|
||||
).toBeVisible();
|
||||
});
|
||||
test('action open mode ', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-table').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
|
||||
//添加按钮
|
||||
await page.getByLabel('Enable actions-Add new').click();
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').click();
|
||||
await expect(await page.locator('.ant-drawer')).toBeVisible();
|
||||
|
||||
//更新按钮打开方式
|
||||
await page.locator('.ant-drawer-mask').click();
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').hover();
|
||||
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-users' }).hover();
|
||||
await page.getByTitle('Open mode').click();
|
||||
await page.getByRole('option', { name: 'Dialog' }).click();
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').click();
|
||||
const drawerComponent = page.getByTestId('modal-Action.Container-users-Add record');
|
||||
await expect(drawerComponent).toBeInViewport();
|
||||
});
|
||||
test('setting action model size', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-table').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
|
||||
//添加按钮
|
||||
await page.getByLabel('Enable actions-Add new').click();
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').click();
|
||||
await expect(await page.locator('.ant-drawer')).toBeVisible();
|
||||
await page.locator('.ant-drawer-mask').click();
|
||||
//修改尺寸
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').hover();
|
||||
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-users' }).hover();
|
||||
await page.getByTitle('Popup size').click();
|
||||
|
||||
//默认尺寸为Middle
|
||||
await expect(await page.getByTitle('Popup size').locator('.ant-select-selection-item').innerText()).toBe('Middle');
|
||||
//设置为large
|
||||
await page.getByRole('option', { name: 'Large' }).click();
|
||||
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').click();
|
||||
const drawerItem = await page.locator('.ant-drawer > .ant-drawer-content-wrapper');
|
||||
const drawerWidth = await drawerItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
const parent = element.parentElement;
|
||||
const parentWidth = window.getComputedStyle(parent).getPropertyValue('width');
|
||||
const percentageWidth = (parseFloat(computedStyle.width) / parseFloat(parentWidth)) * 100;
|
||||
return percentageWidth;
|
||||
});
|
||||
//宽度为70%
|
||||
await expect(drawerWidth).toBe(70);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('action linkage rule', () => {
|
||||
test('action linkage to hide', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page
|
||||
.getByLabel('block-item-CardItem-users-table')
|
||||
.getByRole('button', { name: 'Actions', exact: true })
|
||||
.hover();
|
||||
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-users').hover();
|
||||
await page.getByLabel('Enable actions-View').click();
|
||||
await page.getByLabel('block-item-CardItem-users-table').getByLabel('View').hover();
|
||||
await page.getByLabel('View').getByLabel('designer-schema-settings').hover();
|
||||
await page.getByRole('button', { name: 'Linkage rules' }).click();
|
||||
await page.getByRole('button', { name: 'plus Add linkage rule', exact: true }).click();
|
||||
await page.getByText('Add condition', { exact: true }).click();
|
||||
await page.getByTestId('filter-select-field').click();
|
||||
await page.getByTitle('ID', { exact: true }).getByText('ID').click();
|
||||
await page.getByRole('spinbutton').fill('1');
|
||||
await page.getByText('Add property').click();
|
||||
await page.getByTestId('select-linkage-properties').click();
|
||||
await page.getByText('Hidden').click();
|
||||
await page.locator('.ant-modal').getByRole('button', { name: 'OK' }).click();
|
||||
//配置中,按钮显示半透明
|
||||
const actionItem = page.getByLabel('block-item-CardItem-users-table').getByLabel('View');
|
||||
const inputErrorBorderColor = await actionItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.opacity;
|
||||
});
|
||||
await expect(inputErrorBorderColor).toBe('0.1');
|
||||
//使用中,按钮隐藏
|
||||
await page.getByRole('button', { name: 'highlight' }).click();
|
||||
await expect(page.getByLabel('block-item-CardItem-users-table').getByLabel('View')).not.toBeVisible();
|
||||
});
|
||||
test('action linkage to disabled', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: tablePageSchema }).goto();
|
||||
await page
|
||||
.getByLabel('block-item-CardItem-users-table')
|
||||
.getByRole('button', { name: 'Actions', exact: true })
|
||||
.hover();
|
||||
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-users').hover();
|
||||
await page.getByLabel('Enable actions-View').click();
|
||||
await page.getByLabel('block-item-CardItem-users-table').getByLabel('View').hover();
|
||||
await page.getByLabel('View').getByLabel('designer-schema-settings').hover();
|
||||
await page.getByRole('button', { name: 'Linkage rules' }).click();
|
||||
await page.getByRole('button', { name: 'plus Add linkage rule', exact: true }).click();
|
||||
await page.getByText('Add condition', { exact: true }).click();
|
||||
await page.getByTestId('filter-select-field').click();
|
||||
await page.getByTitle('ID', { exact: true }).getByText('ID').click();
|
||||
await page.getByRole('spinbutton').fill('1');
|
||||
await page.getByText('Add property').click();
|
||||
await page.getByTestId('select-linkage-properties').click();
|
||||
await page.getByText('Disabled').click();
|
||||
await page.locator('.ant-modal').getByRole('button', { name: 'OK' }).click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
const linkBtn = page.getByLabel('block-item-CardItem-users-table').getByLabel('View');
|
||||
const linkBtnCursor = await linkBtn.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.cursor;
|
||||
});
|
||||
//按钮禁用
|
||||
await expect(linkBtnCursor).toBe('not-allowed');
|
||||
});
|
||||
});
|
561
packages/core/client/src/__tests__/e2e/block.test.ts
Normal file
561
packages/core/client/src/__tests__/e2e/block.test.ts
Normal file
@ -0,0 +1,561 @@
|
||||
import { expect, test } from '@nocobase/test/client';
|
||||
|
||||
const tablePageSchema = {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
mwxaaxb9y9v: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
properties: {
|
||||
ibb0kjq3kyl: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
p39cigcjpij: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
'237ec1x538e': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'TableBlockProvider',
|
||||
'x-acl-action': 'users:list',
|
||||
'x-decorator-props': {
|
||||
collection: 'users',
|
||||
resource: 'users',
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 20,
|
||||
},
|
||||
rowKey: 'id',
|
||||
showIndex: true,
|
||||
dragSort: false,
|
||||
disableTemplate: false,
|
||||
},
|
||||
'x-designer': 'TableBlockDesigner',
|
||||
'x-component': 'CardItem',
|
||||
'x-filter-targets': [],
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-initializer': 'TableActionInitializers',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 'var(--nb-spacing)',
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
lmeom75gry5: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-action': 'create',
|
||||
'x-acl-action': 'create',
|
||||
title: "{{t('Add new')}}",
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action',
|
||||
'x-decorator': 'ACLActionProvider',
|
||||
'x-component-props': {
|
||||
openMode: 'drawer',
|
||||
type: 'primary',
|
||||
component: 'CreateRecordAction',
|
||||
icon: 'PlusOutlined',
|
||||
},
|
||||
'x-align': 'right',
|
||||
'x-acl-action-props': {
|
||||
skipScopeCheck: true,
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Add record") }}',
|
||||
'x-component': 'Action.Container',
|
||||
'x-component-props': {
|
||||
className: 'nb-action-popup',
|
||||
},
|
||||
properties: {
|
||||
tabs: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Tabs',
|
||||
'x-component-props': {},
|
||||
'x-initializer': 'TabPaneInitializersForCreateFormBlock',
|
||||
properties: {
|
||||
tab1: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{t("Add new")}}',
|
||||
'x-component': 'Tabs.TabPane',
|
||||
'x-designer': 'Tabs.Designer',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
grid: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'CreateFormBlockInitializers',
|
||||
'x-uid': 'w224zhqyair',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'lll44vre1t6',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'c025dgp5tyk',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'og2z02rfxhx',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'vn9wxzx83y3',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '7s3fcxfjc0y',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
zqdtqsjqgc1: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'array',
|
||||
'x-initializer': 'TableColumnInitializers',
|
||||
'x-component': 'TableV2',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
useProps: '{{ useTableBlockProps }}',
|
||||
},
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Actions") }}',
|
||||
'x-action-column': 'actions',
|
||||
'x-decorator': 'TableV2.Column.ActionBar',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||
'x-initializer': 'TableActionColumnInitializers',
|
||||
properties: {
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
properties: {
|
||||
kdcs236lihl: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Edit") }}',
|
||||
'x-action': 'update',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
openMode: 'drawer',
|
||||
icon: 'EditOutlined',
|
||||
},
|
||||
'x-decorator': 'ACLActionProvider',
|
||||
'x-designer-props': {
|
||||
linkageAction: true,
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{ t("Edit record") }}',
|
||||
'x-component': 'Action.Container',
|
||||
'x-component-props': {
|
||||
className: 'nb-action-popup',
|
||||
},
|
||||
properties: {
|
||||
tabs: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Tabs',
|
||||
'x-component-props': {},
|
||||
'x-initializer': 'TabPaneInitializers',
|
||||
properties: {
|
||||
tab1: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: '{{t("Edit")}}',
|
||||
'x-component': 'Tabs.TabPane',
|
||||
'x-designer': 'Tabs.Designer',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
grid: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'RecordBlockInitializers',
|
||||
'x-uid': 's49vs6v3qs0',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '33qff1grgqn',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '3z1hbrs3bre',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '09vwzm2det2',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'bgdfnken9ua',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'tn98i5lgydw',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'ubmt489cxzn',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 't6eg1ye4wf4',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': 'h4yvac2sy2g',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '3xei2593vgu',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'm67du7wrojo',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '7aige8a5w3q',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'qpbgsjdsgaq',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
};
|
||||
|
||||
test.describe('add block & delete block', () => {
|
||||
test('add block,then delete block', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
await page.getByLabel('schema-initializer-Grid-BlockInitializers').click();
|
||||
await page.getByLabel('dataBlocks-table', { exact: true }).click();
|
||||
await page.getByLabel('dataBlocks-table-Users').click();
|
||||
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
|
||||
await expect(page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users')).toBeVisible();
|
||||
await expect(await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-users')).toBeVisible();
|
||||
//删除区块
|
||||
await page.getByLabel('block-item-CardItem-users-table').hover();
|
||||
await page
|
||||
.getByLabel('block-item-CardItem-users-table')
|
||||
.getByRole('button', { name: 'designer-schema-settings' })
|
||||
.click();
|
||||
await page.getByLabel('Delete').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await expect(page.getByLabel('block-item-CardItem-users-table')).not.toBeVisible();
|
||||
await expect(page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users')).not.toBeVisible();
|
||||
await expect(await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-users')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('block title', () => {
|
||||
test('edit block title', async ({ page, mockPage }) => {
|
||||
await mockPage({
|
||||
pageSchema: tablePageSchema,
|
||||
}).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-table').click();
|
||||
await page
|
||||
.getByLabel('block-item-CardItem-users-table')
|
||||
.getByRole('button', { name: 'designer-schema-settings' })
|
||||
.hover();
|
||||
await page.getByLabel('Edit block title').click();
|
||||
await page.getByLabel('block-item-Input-users-Block title').click();
|
||||
await page.getByLabel('block-item-Input-users-Block title').getByRole('textbox').fill('block title');
|
||||
await page.locator('.ant-modal').getByRole('button', { name: 'OK' }).click();
|
||||
await expect(page.getByLabel('block-item-CardItem-users-table').locator('.ant-card-head')).toBeVisible();
|
||||
await expect(await page.getByLabel('block-item-CardItem-users-table').locator('.ant-card-head').innerText()).toBe(
|
||||
'block title',
|
||||
);
|
||||
|
||||
//回显
|
||||
await page
|
||||
.getByLabel('block-item-CardItem-users-table')
|
||||
.getByRole('button', { name: 'designer-schema-settings' })
|
||||
.click();
|
||||
await page.getByText('Edit block title').click();
|
||||
const inputValue = await page.getByRole('textbox').inputValue();
|
||||
await expect(inputValue).toBe('block title');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('blcok template', () => {
|
||||
test('save block template', async ({ page, mockPage }) => {
|
||||
await mockPage({
|
||||
name: 'block template source',
|
||||
pageSchema: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
bg76x03o9f2: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
properties: {
|
||||
gdj0ceke8ac: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
ftx8xnesvev: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
tu0dxua38tw: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-acl-action-props': {
|
||||
skipScopeCheck: true,
|
||||
},
|
||||
'x-acl-action': 'users:create',
|
||||
'x-decorator': 'FormBlockProvider',
|
||||
'x-decorator-props': {
|
||||
resource: 'users',
|
||||
collection: 'users',
|
||||
},
|
||||
'x-designer': 'FormV2.Designer',
|
||||
'x-component': 'CardItem',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
avv3vpk0nlv: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'FormV2',
|
||||
'x-component-props': {
|
||||
useProps: '{{ useFormBlockProps }}',
|
||||
},
|
||||
properties: {
|
||||
grid: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'FormItemInitializers',
|
||||
properties: {
|
||||
gnw25oyqe56: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
rdbe3gg1qv5: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
nickname: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'string',
|
||||
'x-designer': 'FormItem.Designer',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': 'users.nickname',
|
||||
'x-component-props': {},
|
||||
'x-uid': 'okrljzl6j7s',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '1zjdduck27k',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'l0cyy3gzz86',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '4wrgwkyyf81',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-initializer': 'FormActionInitializers',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
style: {
|
||||
marginTop: 24,
|
||||
},
|
||||
},
|
||||
'x-uid': '2apymtcq35d',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': '1tnmbrvb9ad',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'vo1pyqmoe28',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'z59pkpc8uhq',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'vsfafj9qcx9',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'sdj6iw5b0gs',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'uz4dyz41vt1',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
},
|
||||
}).goto();
|
||||
await page.getByLabel('block-item-CardItem-users-form').click();
|
||||
await page
|
||||
.getByLabel('block-item-CardItem-users-form')
|
||||
.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-users')
|
||||
.click();
|
||||
await page.getByLabel('Save as block template').click();
|
||||
await page.getByLabel('Save as template').getByRole('textbox').fill('Users_Form');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByLabel('block-item-CardItem-users-form').hover();
|
||||
await page.waitForTimeout(1000);
|
||||
//保存模板后当前区块为引用区块
|
||||
const titleTag = await page.getByLabel('block-item-CardItem-users-form').locator('.title-tag').nth(1).innerText();
|
||||
await expect(titleTag).toContain('Reference template');
|
||||
});
|
||||
test('using block template ', async ({ page, mockPage }) => {
|
||||
await mockPage({
|
||||
name: 'block template',
|
||||
pageSchema: tablePageSchema,
|
||||
}).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-BlockInitializers').click();
|
||||
//使用复制模板
|
||||
await page.getByLabel('dataBlocks-form', { exact: true }).click();
|
||||
await page.getByLabel('dataBlocks-form-FormItem_table_subMenu').click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate template right' }).click();
|
||||
await page.getByText('Users_Form (Fields only)').first().click();
|
||||
await expect(page.getByLabel('block-item-CardItem-users-form')).toBeVisible();
|
||||
|
||||
//在新建操作中使用引用模板
|
||||
await page.getByLabel('action-Action-Add new-create-users-table').click();
|
||||
await page.getByLabel('schema-initializer-Grid-CreateFormBlockInitializers-users').click();
|
||||
await page.getByRole('menuitem', { name: 'form Form right' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Reference template right' }).click();
|
||||
await page.getByRole('button', { name: 'Users_Form (Fields only)' }).first().click();
|
||||
await page.getByLabel('schema-initializer-Grid-CreateFormBlockInitializers-users').hover();
|
||||
await expect(page.locator('.ant-drawer').getByLabel('block-item-CardItem-users-form')).toBeVisible();
|
||||
await page.locator('.ant-drawer-mask').click();
|
||||
|
||||
//在编辑操作中使用引用模板
|
||||
await page.getByLabel('block-item-CardItem-users-table').getByLabel('Edit').click();
|
||||
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-users').click();
|
||||
await page.getByRole('menuitem', { name: 'form Form right' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Reference template right' }).click();
|
||||
await page.getByRole('button', { name: 'Users_Form (Fields only)' }).first().click();
|
||||
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-users').hover();
|
||||
|
||||
//修改引用模板
|
||||
await page.locator('.ant-drawer').getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Phone').click();
|
||||
await page.locator('.ant-drawer-mask').click();
|
||||
//复制模板不同步,引用模板同步
|
||||
await expect(
|
||||
page.getByLabel('block-item-CardItem-users-form').getByLabel('block-item-CollectionField-users-form-users.phone'),
|
||||
).not.toBeVisible();
|
||||
await page.getByLabel('block-item-CardItem-users-table').getByLabel('action-Action-Add').click();
|
||||
await expect(await page.getByLabel('block-item-CollectionField-users-form-users.phone')).toBeVisible();
|
||||
await page.locator('.ant-drawer-mask').click();
|
||||
|
||||
//删除模板
|
||||
await page.getByTestId('settings-center-button').click();
|
||||
await page.getByRole('button', { name: 'All plugin settings' }).click();
|
||||
await page.getByRole('menuitem', { name: 'layout Block templates' }).click();
|
||||
await page.getByLabel('action-Action.Link-Delete-destroy-uiSchemaTemplates-table-Users_Form').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
});
|
||||
});
|
349
packages/core/client/src/__tests__/e2e/field.test.ts
Normal file
349
packages/core/client/src/__tests__/e2e/field.test.ts
Normal file
@ -0,0 +1,349 @@
|
||||
import { expect, test } from '@nocobase/test/client';
|
||||
import { approximateColor } from './utils';
|
||||
const formPageSchema = {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
lyie34rtcu4: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
properties: {
|
||||
skmxkfr67em: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
'860xnb3egb8': {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
aa9rqm7bkud: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-acl-action-props': {
|
||||
skipScopeCheck: true,
|
||||
},
|
||||
'x-acl-action': 'users:create',
|
||||
'x-decorator': 'FormBlockProvider',
|
||||
'x-decorator-props': {
|
||||
resource: 'users',
|
||||
collection: 'users',
|
||||
},
|
||||
'x-designer': 'FormV2.Designer',
|
||||
'x-component': 'CardItem',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
t4mi2jywqj7: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'FormV2',
|
||||
'x-component-props': {
|
||||
useProps: '{{ useFormBlockProps }}',
|
||||
},
|
||||
properties: {
|
||||
grid: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'FormItemInitializers',
|
||||
'x-uid': '66dozhzo5ld',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
actions: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-initializer': 'FormActionInitializers',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
style: {
|
||||
marginTop: 24,
|
||||
},
|
||||
},
|
||||
'x-uid': 'q8am9zt6m2x',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-uid': '4ojxya0re8h',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '183n4o77awp',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'zwxgkj66isi',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '8tryd9kz9bd',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': 'piwq6rrjrsq',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
},
|
||||
'x-uid': '5cszm46yqxb',
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
};
|
||||
|
||||
test.describe('add field & remove field in block', () => {
|
||||
test('add field,then remove field in block', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Nickname').click();
|
||||
await page.getByLabel('Display collection fields-Username').click();
|
||||
await page.getByLabel('Display collection fields-Email').click();
|
||||
//添加字段
|
||||
await expect(page.getByLabel('block-item-CollectionField-users-form-users.nickname')).toBeVisible();
|
||||
await expect(page.getByLabel('block-item-CollectionField-users-form-users.username')).toBeVisible();
|
||||
await expect(page.getByLabel('block-item-CollectionField-users-form-users.email')).toBeVisible();
|
||||
//激活的状态
|
||||
await expect(await page.getByLabel('Display collection fields-Nickname').getByRole('switch').isChecked()).toBe(
|
||||
true,
|
||||
);
|
||||
await expect(await page.getByLabel('Display collection fields-Username').getByRole('switch').isChecked()).toBe(
|
||||
true,
|
||||
);
|
||||
await expect(await page.getByLabel('Display collection fields-Email').getByRole('switch').isChecked()).toBe(true);
|
||||
//移除字段
|
||||
await page.getByLabel('Display collection fields-Nickname').click();
|
||||
await page.getByLabel('Display collection fields-Username').click();
|
||||
await page.getByLabel('Display collection fields-Email').click();
|
||||
await expect(page.getByLabel('block-item-CollectionField-users-form-users.nickname')).not.toBeVisible();
|
||||
await expect(page.getByLabel('block-item-CollectionField-users-form-users.username')).not.toBeVisible();
|
||||
await expect(page.getByLabel('block-item-CollectionField-users-form-users.email')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('drag field in block', () => {
|
||||
test('drag field for layout', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Nickname').click();
|
||||
await page.getByLabel('Display collection fields-Username').click();
|
||||
await page.getByLabel('Display collection fields-Email').click();
|
||||
|
||||
const sourceElement = await page.getByLabel('block-item-CollectionField-users-form-users.nickname');
|
||||
await sourceElement.hover();
|
||||
const source = await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.getByLabel('designer-drag');
|
||||
await source.hover();
|
||||
|
||||
const targetElement = await page.getByLabel('block-item-CollectionField-users-form-users.username');
|
||||
await source.dragTo(targetElement);
|
||||
const targetElement2 = await page.getByLabel('block-item-CollectionField-users-form-users.email');
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').getByLabel('designer-drag').hover();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.getByLabel('designer-drag')
|
||||
.dragTo(targetElement2);
|
||||
|
||||
const nickname = await source.boundingBox();
|
||||
const username = await targetElement.boundingBox();
|
||||
const email = await targetElement2.boundingBox();
|
||||
const max = Math.max(username.y, nickname.y, email.y);
|
||||
//拖拽调整排序符合预期
|
||||
await expect(nickname.y).toBe(max);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('field setting config ', () => {
|
||||
test('edit field label in block', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Username').click();
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.username').hover();
|
||||
await page.getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-users-users.username').click();
|
||||
// await page.getByLabel('block-item-CollectionField-users-form-users.username').getByLabel('designer-schema-settings').click();
|
||||
await page.getByLabel('Edit field title').click();
|
||||
await page.getByLabel('block-item-Input-users-Field title').getByRole('textbox').fill('Username1');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await expect(
|
||||
page.getByLabel('block-item-CollectionField-users-form-users.username').getByText('Username1'),
|
||||
).toBeVisible();
|
||||
//回显
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.username').click();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.username')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.hover();
|
||||
await page.getByLabel('Edit field title').click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
await expect(await page.getByLabel('block-item-Input-users-Field title').getByRole('textbox').inputValue()).toBe(
|
||||
'Username1',
|
||||
);
|
||||
});
|
||||
test('display & not display field label in block ', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Username').click();
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.username').click();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.username')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.hover();
|
||||
await page.getByLabel('Display title').hover();
|
||||
//默认显示
|
||||
await expect(await page.getByLabel('Display title').getByRole('switch').isChecked()).toBe(true);
|
||||
//设置不显示标题
|
||||
await page.getByLabel('Display title').click();
|
||||
const labelItem = page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.username')
|
||||
.locator('.ant-formily-item-label');
|
||||
const labelDisplay = await labelItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.display;
|
||||
});
|
||||
expect(labelDisplay).toBe('none');
|
||||
//设置显示标题
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.username').click();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.username')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.hover();
|
||||
await page.getByLabel('Display title').click();
|
||||
const labelDisplay1 = await labelItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.display;
|
||||
});
|
||||
expect(labelDisplay1).toBe('flex');
|
||||
});
|
||||
test('edit field description ', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
const description = 'field description';
|
||||
const descriptionColor = 'rgba(0, 0, 0, 0.65)';
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Username').click();
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.username').click();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.username')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.hover();
|
||||
await page.getByLabel('Edit description').click();
|
||||
await page.getByLabel('block-item-Input.TextArea-users').locator('textarea').fill(description);
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
const descriptionItem = await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.username')
|
||||
.locator('.ant-formily-item-extra');
|
||||
const descriptionItemColor = await descriptionItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.color;
|
||||
});
|
||||
//字段描述样式符合预期
|
||||
expect(await descriptionItem.innerText()).toBe(description);
|
||||
const isApproximate = approximateColor(descriptionItemColor, descriptionColor);
|
||||
await expect(isApproximate).toBe(true);
|
||||
});
|
||||
test('field required ', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Nickname').click();
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').click();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.click();
|
||||
await page.getByLabel('Required').click();
|
||||
await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-users').click();
|
||||
await page.getByLabel('Enable actions-Submit').click();
|
||||
await page.getByLabel('action-Action-Submit-submit-users-form').click();
|
||||
await page.waitForTimeout(1000);
|
||||
//必填校验符合预期
|
||||
await expect(
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.locator('.ant-formily-item-error-help'),
|
||||
).toBeVisible();
|
||||
const inputItem = page.getByLabel('block-item-CollectionField-users-form-users.nickname').locator('input');
|
||||
const inputErrorBorderColor = await inputItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.borderColor;
|
||||
});
|
||||
//样式符合预期
|
||||
expect(inputErrorBorderColor).toBe('rgb(255, 77, 79)');
|
||||
// 断言表单未被提交
|
||||
expect(await page.getByLabel('block-item-CardItem-users-form').locator('form')).not.toHaveProperty(
|
||||
'submitted',
|
||||
true,
|
||||
);
|
||||
});
|
||||
test('field validation rule ', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
const errorMessage = 'this is error message';
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Nickname').click();
|
||||
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').click();
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.click();
|
||||
await page.getByText('Set validation rules').click();
|
||||
await page.getByRole('button', { name: 'plus Add validation rule' }).click();
|
||||
await await page.getByLabel('block-item-InputNumber-users-Max length').getByRole('spinbutton').fill('3');
|
||||
await page.getByRole('button', { name: 'Error message' }).locator('textarea').fill(errorMessage);
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').getByRole('textbox').fill('1111');
|
||||
const errorInfo = await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.locator('.ant-formily-item-error-help');
|
||||
await expect(errorInfo).toBeVisible();
|
||||
//报错信息符合预期
|
||||
await expect(await errorInfo.innerText()).toBe(errorMessage);
|
||||
await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-users').click();
|
||||
await page.getByLabel('Enable actions-Submit').click();
|
||||
await page.getByLabel('action-Action-Submit-submit-users-form').click();
|
||||
await page.waitForTimeout(1000);
|
||||
// 断言表单未被提交
|
||||
expect(await page.getByLabel('block-item-CardItem-users-form').locator('form')).not.toHaveProperty(
|
||||
'submitted',
|
||||
true,
|
||||
);
|
||||
});
|
||||
test('field pattern ', async ({ page, mockPage }) => {
|
||||
await mockPage({ pageSchema: formPageSchema }).goto();
|
||||
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
|
||||
await page.getByLabel('Display collection fields-Nickname').click();
|
||||
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').click();
|
||||
const inputElement = await page.getByLabel('block-item-CollectionField-users-form-users.nickname').locator('input');
|
||||
await page
|
||||
.getByLabel('block-item-CollectionField-users-form-users.nickname')
|
||||
.getByLabel('designer-schema-settings')
|
||||
.click();
|
||||
await page.getByLabel('Pattern').click();
|
||||
|
||||
//禁用
|
||||
await page.getByRole('option', { name: 'Readonly' }).click();
|
||||
await expect(await inputElement.isDisabled()).toBe(true);
|
||||
await page.getByLabel('Pattern').click();
|
||||
//只读
|
||||
await page.getByText('Easy-reading').click();
|
||||
await expect(
|
||||
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').locator('.ant-description-input'),
|
||||
).toBeInViewport();
|
||||
});
|
||||
});
|
@ -1,22 +1,217 @@
|
||||
import { enableToConfig, expect, test } from '@nocobase/test/client';
|
||||
import { approximateColor } from './utils';
|
||||
|
||||
test.describe('menu', () => {
|
||||
test('create new page, then delete', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await enableToConfig(page);
|
||||
|
||||
await page.getByLabel('schema-initializer-Menu-header').hover();
|
||||
await page.getByLabel('Page').click();
|
||||
test.describe('menu page', () => {
|
||||
test('create new page, then delete', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
const pageTitle = 'new page';
|
||||
await page.getByLabel('schema-initializer-Menu-header').click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
await page.getByRole('menu').getByLabel('Page').click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill('new page');
|
||||
await page.getByRole('textbox').fill(pageTitle);
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByText('new page').click();
|
||||
|
||||
const menuItem = page.getByRole('menu').locator('li').filter({ hasText: pageTitle });
|
||||
const defaultBackgroundColor = await menuItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.backgroundColor;
|
||||
});
|
||||
await menuItem.click();
|
||||
// 获取激活后的背景高亮颜色
|
||||
const activedBackgroundColor = await menuItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.backgroundColor;
|
||||
});
|
||||
// 菜单高亮/进入空页面只有一个add block 按钮
|
||||
expect(activedBackgroundColor).not.toBe(defaultBackgroundColor);
|
||||
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
|
||||
await expect(page.getByTitle(pageTitle)).toBeVisible();
|
||||
const divElement = await page.locator('.nb-page-content');
|
||||
const buttons = await divElement.locator('button').count();
|
||||
const divText = await divElement.textContent();
|
||||
|
||||
await expect(buttons).toEqual(1);
|
||||
await expect(divText).toBe('Add block');
|
||||
|
||||
// 删除页面,避免影响其他测试
|
||||
await page.getByRole('menu').getByText('new page').hover();
|
||||
await page.getByLabel('designer-schema-settings-Menu.Item').hover();
|
||||
await page.getByLabel(pageTitle).click();
|
||||
await page.getByLabel(pageTitle).getByLabel('designer-schema-settings').hover();
|
||||
await page.getByLabel('Delete').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await mockPage().goto();
|
||||
await expect(page.getByTitle(pageTitle)).not.toBeVisible();
|
||||
});
|
||||
test('edit menu title', async ({ page, mockPage }) => {
|
||||
const pageTitle = 'page title';
|
||||
const newPagetitle = 'page title1';
|
||||
await mockPage({
|
||||
name: pageTitle,
|
||||
}).goto();
|
||||
await page.getByLabel(pageTitle).hover();
|
||||
await page.getByLabel(pageTitle).getByLabel('designer-schema-settings').hover();
|
||||
await page.getByLabel('Edit').click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill(newPagetitle);
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByRole('menu').getByText(newPagetitle).click();
|
||||
await mockPage().goto();
|
||||
await page.getByText(newPagetitle).click();
|
||||
await expect(page.getByTitle(newPagetitle)).toBeVisible();
|
||||
});
|
||||
test('move menu ', async ({ page, mockPage }) => {
|
||||
const pageTitle1 = 'page1';
|
||||
const pageTitle2 = 'page2';
|
||||
await mockPage({
|
||||
name: pageTitle1,
|
||||
}).goto();
|
||||
await mockPage({ name: pageTitle2 }).goto();
|
||||
await enableToConfig(page);
|
||||
await page.getByRole('menu').getByText(pageTitle1).click();
|
||||
await page.getByRole('menu').getByText(pageTitle1).hover();
|
||||
await page.getByLabel(pageTitle1).getByLabel('designer-schema-settings').hover();
|
||||
await page.getByLabel('Move to').click();
|
||||
await page.getByRole('dialog').click();
|
||||
await page.getByLabel('Search').click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
await page.locator('.ant-select-dropdown').getByText(pageTitle2).click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
const page1 = await page.getByRole('menu').getByText(pageTitle1).boundingBox();
|
||||
const page2 = await page.getByRole('menu').getByText(pageTitle2).boundingBox();
|
||||
//拖拽菜单排序符合预期
|
||||
expect(page2.x).toBeLessThan(page1.x);
|
||||
});
|
||||
|
||||
test('insert page before', async ({ page, mockPage }) => {
|
||||
const pageTitle3 = 'page3';
|
||||
const pageTitle4 = 'page4';
|
||||
await mockPage({
|
||||
name: pageTitle3,
|
||||
}).goto();
|
||||
|
||||
await page.getByLabel(pageTitle3).click();
|
||||
await page.getByLabel(pageTitle3).hover();
|
||||
await page.getByLabel(pageTitle3).getByLabel('designer-schema-settings').hover();
|
||||
|
||||
await page.getByLabel('Insert before').hover();
|
||||
|
||||
await page.getByRole('button', { name: 'Add page' }).click();
|
||||
await page.getByRole('textbox').fill(pageTitle4);
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
|
||||
const page3 = await page.getByLabel(pageTitle3).boundingBox();
|
||||
const page4 = await page.getByLabel(pageTitle4).boundingBox();
|
||||
//插入的菜单位置符合预期,且进入空页面
|
||||
expect(page4.x).toBeLessThan(page3.x);
|
||||
await page.getByRole('menu').getByText(pageTitle4).click();
|
||||
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
|
||||
//删除页面
|
||||
await page.getByLabel(pageTitle4).click();
|
||||
await page.getByLabel(pageTitle4).getByLabel('designer-schema-settings').hover();
|
||||
await page.getByLabel('Delete').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
});
|
||||
|
||||
test('insert page after', async ({ page, mockPage }) => {
|
||||
const pageTitle5 = 'page5';
|
||||
const pageTitle6 = 'page6';
|
||||
await mockPage({
|
||||
name: pageTitle5,
|
||||
}).goto();
|
||||
|
||||
await page.getByLabel(pageTitle5).click();
|
||||
await page.getByLabel(pageTitle5).hover();
|
||||
await page.getByLabel(pageTitle5).getByLabel('designer-schema-settings').hover();
|
||||
|
||||
await page.getByLabel('Insert after').hover();
|
||||
|
||||
await page.getByRole('button', { name: 'Add page' }).click();
|
||||
await page.getByRole('textbox').fill(pageTitle6);
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
|
||||
const page6 = await page.getByLabel(pageTitle6).boundingBox();
|
||||
const page5 = await page.getByLabel(pageTitle5).boundingBox();
|
||||
//插入的菜单位置符合预期
|
||||
expect(page5.x).toBeLessThan(page6.x);
|
||||
await page.getByLabel(pageTitle6).click();
|
||||
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
|
||||
//删除页面
|
||||
await page.getByLabel(pageTitle6).getByLabel('designer-schema-settings-Menu.Item').click();
|
||||
await page.getByRole('menu').getByLabel('Delete').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('menu group', () => {
|
||||
test('create new menu group, then delete', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
await page.getByLabel('schema-initializer-Menu-header').hover();
|
||||
await page.getByRole('menu').getByLabel('Group').click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill('menu Group');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByText('menu Group').click();
|
||||
|
||||
await expect(page.getByLabel('schema-initializer-Menu-side')).toBeVisible();
|
||||
const sideBar = await page.locator('ul').filter({ hasText: /^Add menu item$/ });
|
||||
await expect(sideBar).toBeVisible();
|
||||
|
||||
//添加子页面
|
||||
await page
|
||||
.locator('ul')
|
||||
.filter({ hasText: /^Add menu item$/ })
|
||||
.click();
|
||||
await page.getByLabel('schema-initializer-Menu-side').click();
|
||||
await page.getByRole('button', { name: 'Page' }).click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill('group page');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByText('group page').click();
|
||||
//进入子页面
|
||||
await expect(page.getByTitle('group page')).toBeVisible();
|
||||
//对应分组/子菜单项均高亮
|
||||
const menuItem = await page.getByRole('menu').locator('li').filter({ hasText: 'menu Group' });
|
||||
const menuItemBackgroundColor = await menuItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.backgroundColor;
|
||||
});
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
const isApproximate = approximateColor(menuItemBackgroundColor, 'rgba(255, 255, 255, 0.1)');
|
||||
await expect(isApproximate).toBe(true);
|
||||
const pageItem = await page.getByRole('menu').locator('li').filter({ hasText: 'group page' });
|
||||
|
||||
const pageItemBackgroundColor = await pageItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.backgroundColor;
|
||||
});
|
||||
await expect(pageItemBackgroundColor).toBe('rgb(230, 244, 255)');
|
||||
|
||||
// 删除页面,避免影响其他测试
|
||||
await page.getByLabel('menu Group').click();
|
||||
await page.getByLabel('menu Group').hover();
|
||||
await page.getByLabel('menu Group').getByLabel('designer-schema-settings').first().hover();
|
||||
await page.getByLabel('Delete').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await mockPage().goto();
|
||||
await expect(page.getByTitle('menu Group')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('menu link', () => {
|
||||
test('create new menu link, then delete', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
await page.getByLabel('schema-initializer-Menu-header').hover();
|
||||
await page.getByRole('menu').getByLabel('Link').click();
|
||||
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').fill('link menu');
|
||||
await page.getByLabel('block-item-Input-Link').getByRole('textbox').fill('https://www.baidu.com/');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByLabel('link menu').click();
|
||||
const page2Promise = page.waitForEvent('popup');
|
||||
const page2 = await page2Promise;
|
||||
|
||||
await expect(page2.getByRole('button', { name: '百度一下' })).toBeVisible();
|
||||
// 删除页面,避免影响其他测试
|
||||
await page.getByLabel('link menu').click();
|
||||
await page.getByLabel('link menu').getByLabel('designer-schema-settings').hover();
|
||||
await page.getByLabel('Delete').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
});
|
||||
|
189
packages/core/client/src/__tests__/e2e/page.test.ts
Normal file
189
packages/core/client/src/__tests__/e2e/page.test.ts
Normal file
@ -0,0 +1,189 @@
|
||||
import { enableToConfig, expect, test } from '@nocobase/test/client';
|
||||
|
||||
test.describe('page header', () => {
|
||||
test('disabled & enabled page header', async ({ page, mockPage }) => {
|
||||
const pageTitle = 'page header';
|
||||
await mockPage({ name: pageTitle }).goto();
|
||||
//默认开启
|
||||
await expect(page.getByTitle(pageTitle)).toBeVisible();
|
||||
await page.getByTitle(pageTitle).click();
|
||||
await page.getByLabel('designer-schema-settings-Page').hover();
|
||||
await expect(page.getByLabel('Enable page header').getByRole('switch')).toBeChecked();
|
||||
//关闭
|
||||
await page.getByLabel('Enable page header').getByRole('switch').click();
|
||||
await expect(await page.locator('.ant-page-header')).not.toBeVisible();
|
||||
await expect(page.getByLabel('Enable page header').getByRole('switch')).not.toBeChecked();
|
||||
//开启
|
||||
await page.getByRole('main').locator('span').nth(1).click();
|
||||
await page.getByLabel('designer-schema-settings-Page').hover();
|
||||
await page.getByLabel('Enable page header').getByRole('switch').click();
|
||||
await expect(await page.locator('.ant-page-header').getByTitle(pageTitle)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('page title', () => {
|
||||
test('disable & not disable page title', async ({ page, mockPage }) => {
|
||||
const pageTitle = 'page title';
|
||||
await mockPage({ name: pageTitle }).goto();
|
||||
//默认显示
|
||||
await expect(page.getByTitle(pageTitle)).toBeVisible();
|
||||
await page.getByTitle(pageTitle).click();
|
||||
await page.getByLabel('designer-schema-settings-Page').hover();
|
||||
await expect(page.getByLabel('Display page title').getByRole('switch')).toBeChecked();
|
||||
//不显示
|
||||
await page.getByLabel('Display page title').getByRole('switch').click();
|
||||
await expect(page.locator('.ant-page-header')).toBeVisible();
|
||||
await expect(page.locator('.ant-page-header').getByTitle(pageTitle)).not.toBeVisible();
|
||||
await expect(page.getByLabel('Display page title').getByRole('switch')).not.toBeChecked();
|
||||
//开启
|
||||
await page.locator('.ant-page-header').click();
|
||||
await page.getByLabel('designer-schema-settings-Page').hover();
|
||||
await page.getByLabel('Display page title').getByRole('switch').click();
|
||||
await expect(page.locator('.ant-page-header').getByTitle(pageTitle)).toBeVisible();
|
||||
});
|
||||
test('edit page title', async ({ page, mockPage }) => {
|
||||
await mockPage({ name: 'page title1' }).goto();
|
||||
|
||||
await expect(page.getByTitle('page title1')).toBeVisible();
|
||||
await page.getByTitle('page title1').click();
|
||||
await page.getByLabel('designer-schema-settings-Page').hover();
|
||||
await page.getByText('Edit page title').click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill('page title2');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByText('page title2').click();
|
||||
await expect(page.getByText('page title2')).toBeVisible();
|
||||
//菜单栏不调整
|
||||
await expect(page.getByLabel('page title1')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('page tabs', () => {
|
||||
test('enable & disabled page tab', async ({ page, mockPage }) => {
|
||||
await mockPage({ name: 'page tab' }).goto();
|
||||
await page.getByTitle('page tab').click();
|
||||
await page.getByLabel('designer-schema-settings-Page').hover();
|
||||
//默认不启用
|
||||
await expect(page.getByLabel('Enable page tabs').getByRole('switch')).not.toBeChecked();
|
||||
//启用标签
|
||||
await page.getByLabel('Enable page tabs').click();
|
||||
await expect(page.getByRole('tab').locator('div').filter({ hasText: 'Unnamed' })).toBeVisible();
|
||||
await expect(page.getByLabel('schema-initializer-Page-tabs')).toBeVisible();
|
||||
await page.getByRole('tab').locator('div').filter({ hasText: 'Unnamed' }).click();
|
||||
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
|
||||
|
||||
//添加新的tab
|
||||
await page.getByLabel('schema-initializer-Page-tabs').click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill('page tab 1');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByText('page tab 1').click();
|
||||
await page.getByLabel('schema-initializer-Page-tabs').click();
|
||||
await page.getByRole('textbox').click();
|
||||
await page.getByRole('textbox').fill('page tab 2');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await page.getByText('page tab 2').click();
|
||||
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
const tabMenuItem = await page.getByRole('tab').locator('div > span').filter({ hasText: 'page tab 2' });
|
||||
const tabMenuItemActivedColor = await tabMenuItem.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.color;
|
||||
});
|
||||
//激活的tab样式符合预期
|
||||
await expect(page.getByText('page tab 1')).toBeVisible();
|
||||
await expect(page.getByText('page tab 2')).toBeVisible();
|
||||
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
|
||||
await expect(tabMenuItemActivedColor).toBe('rgb(22, 119, 255)');
|
||||
|
||||
//修改tab名称
|
||||
await page.getByText('Unnamed').click();
|
||||
await page.getByRole('button', { name: 'designer-schema-settings-Page-tab' }).click();
|
||||
await page.getByLabel('Edit tab').click();
|
||||
await page.getByRole('textbox').fill('page tab');
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
|
||||
const tabMenuItem1 = await page.getByRole('tab').getByText('page tab', { exact: true });
|
||||
const tabMenuItemActivedColor1 = await tabMenuItem1.evaluate((element) => {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return computedStyle.color;
|
||||
});
|
||||
await expect(tabMenuItem1).toBeVisible();
|
||||
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
|
||||
await expect(tabMenuItemActivedColor1).toBe('rgb(22, 119, 255)');
|
||||
|
||||
//删除 tab
|
||||
await page.getByRole('tab').getByText('page tab', { exact: true }).hover();
|
||||
await page.getByRole('button', { name: 'designer-schema-settings-Page-tab' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
await expect(page.getByRole('tab').getByText('page tab', { exact: true })).not.toBeVisible();
|
||||
await page.getByRole('tab').getByText('page tab 1').click();
|
||||
|
||||
//禁用标签
|
||||
await page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^page tab$/ })
|
||||
.nth(1)
|
||||
.click();
|
||||
await page.getByLabel('designer-schema-settings-Page', { exact: true }).click();
|
||||
await page.getByLabel('Enable page tabs').getByRole('switch').setChecked(false);
|
||||
await expect(page.getByText('page tab 2')).not.toBeVisible();
|
||||
});
|
||||
test('drag page tab sorting', async ({ page, mockPage }) => {
|
||||
await mockPage({
|
||||
pageSchema: {
|
||||
'x-uid': 'h8q2mcgo3cq',
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Page',
|
||||
'x-component-props': {
|
||||
enablePageTabs: true,
|
||||
},
|
||||
properties: {
|
||||
bi8ep3svjee: {
|
||||
'x-uid': '9kr7xm9x4ln',
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
title: 'tab 1',
|
||||
'x-async': false,
|
||||
'x-index': 1,
|
||||
},
|
||||
rw91udnzpr3: {
|
||||
_isJSONSchemaObject: true,
|
||||
version: '2.0',
|
||||
type: 'void',
|
||||
title: 'tab 2',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
'x-uid': 'o5vp90rqsjx',
|
||||
'x-async': false,
|
||||
'x-index': 2,
|
||||
},
|
||||
},
|
||||
'x-async': true,
|
||||
'x-index': 1,
|
||||
},
|
||||
}).goto();
|
||||
|
||||
const sourceElement = await page.locator('span:has-text("tab 2")');
|
||||
await sourceElement.hover();
|
||||
const source = await page.getByRole('button', { name: 'drag' });
|
||||
await source.hover();
|
||||
const targetElement = await page.locator('span:has-text("tab 1")');
|
||||
const sourceBoundingBox = await sourceElement.boundingBox();
|
||||
const targetBoundingBox = await targetElement.boundingBox();
|
||||
//拖拽标签调整排序 拖拽前 1-2
|
||||
expect(targetBoundingBox.x).toBeLessThan(sourceBoundingBox.x);
|
||||
await source.dragTo(targetElement);
|
||||
await sourceElement.dragTo(targetElement);
|
||||
const tab2 = await page.locator('span:has-text("tab 2")').boundingBox();
|
||||
const tab1 = await page.locator('span:has-text("tab 1")').boundingBox();
|
||||
//拖拽后 2-1
|
||||
await expect(tab2.x).toBeLessThan(tab1.x);
|
||||
});
|
||||
});
|
121
packages/core/client/src/__tests__/e2e/pm.test.ts
Normal file
121
packages/core/client/src/__tests__/e2e/pm.test.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { expect, test } from '@nocobase/test/client';
|
||||
|
||||
async function waitForModalToBeHidden(page) {
|
||||
await page.waitForFunction(() => {
|
||||
const modal = document.querySelector('.ant-modal');
|
||||
if (modal) {
|
||||
const computedStyle = window.getComputedStyle(modal);
|
||||
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
|
||||
}
|
||||
return true; // 如果找不到modal,也算作不可见
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('add plugin in front', () => {
|
||||
test('add plugin npm registry,then remove plugin', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
await page.getByTestId('plugin-manager-button').click();
|
||||
await expect(page.getByLabel('sample-custom-collection-template')).not.toBeVisible();
|
||||
await page.getByRole('button', { name: 'Add new' }).click();
|
||||
await page
|
||||
.getByLabel('block-item-Input-Npm package name')
|
||||
.getByRole('textbox')
|
||||
.fill('@nocobase/plugin-sample-custom-collection-template');
|
||||
await page.getByLabel('Submit').click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
//等待页面刷新结束
|
||||
await page.waitForFunction(() => {
|
||||
const modal = document.querySelector('.ant-modal');
|
||||
if (modal) {
|
||||
const computedStyle = window.getComputedStyle(modal);
|
||||
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
|
||||
}
|
||||
return true; // 如果找不到modal,也算作不可见
|
||||
});
|
||||
await page.waitForLoadState('load');
|
||||
await page.getByPlaceholder('Search plugin').fill('sample-custom-collection-template');
|
||||
await expect(page.getByLabel('sample-custom-collection-template')).toBeVisible();
|
||||
//将添加的插件删除
|
||||
await page.getByLabel('sample-custom-collection-template').getByText('Remove').click();
|
||||
await page.getByRole('button', { name: 'Yes' }).click();
|
||||
await page.waitForTimeout(2000); // 等待2秒钟
|
||||
//等待页面刷新结束
|
||||
await waitForModalToBeHidden(page);
|
||||
await page.waitForLoadState('load');
|
||||
await page.getByPlaceholder('Search plugin').fill('sample-custom-collection-template');
|
||||
await expect(page.getByLabel('sample-custom-collection-template')).not.toBeVisible();
|
||||
});
|
||||
test.skip('add plugin local upload', async ({ page, mockPage }) => {});
|
||||
test.skip('add plugin file url', async ({ page, mockPage }) => {});
|
||||
});
|
||||
|
||||
test.describe('remove plugin', () => {
|
||||
test('remove plugin,then add plugin', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
await page.getByTestId('plugin-manager-button').click();
|
||||
//hello插件默认安装未启用
|
||||
await page.getByPlaceholder('Search plugin').fill('Hello');
|
||||
await expect(page.getByLabel('Hello')).toBeVisible();
|
||||
const isActive = await page.getByLabel('Hello').getByLabel('enable').isChecked();
|
||||
await expect(isActive).toBe(false);
|
||||
//将hello插件remove
|
||||
await page.getByLabel('Hello').getByText('Remove').click();
|
||||
await page.getByRole('button', { name: 'Yes' }).click();
|
||||
//等待页面刷新结束
|
||||
await page.waitForLoadState('load');
|
||||
await page.getByPlaceholder('Search plugin').fill('hello');
|
||||
await expect(page.getByLabel('Hello')).not.toBeVisible();
|
||||
//将删除的插件加回来
|
||||
await page.getByRole('button', { name: 'Add new' }).click();
|
||||
await page
|
||||
.getByLabel('block-item-Input-Npm package name')
|
||||
.getByRole('textbox')
|
||||
.fill('@nocobase/plugin-sample-hello');
|
||||
await page.getByLabel('Submit').click();
|
||||
await page.waitForTimeout(1000);
|
||||
//等待弹窗消失和页面刷新结束
|
||||
await page.waitForFunction(() => {
|
||||
const modal = document.querySelector('.ant-modal');
|
||||
if (modal) {
|
||||
const computedStyle = window.getComputedStyle(modal);
|
||||
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
|
||||
}
|
||||
return true; // 如果找不到modal,也算作不可见
|
||||
});
|
||||
await page.waitForLoadState('load');
|
||||
await page.getByPlaceholder('Search plugin').fill('hello');
|
||||
await expect(page.getByLabel('Hello')).toBeVisible();
|
||||
//已启用的插件不能remove,如ACL
|
||||
await page.getByPlaceholder('Search plugin').fill('ACL');
|
||||
await expect(page.getByLabel('ACL')).toBeVisible();
|
||||
const isAclActive = await page.getByLabel('ACL').getByLabel('enable').isChecked();
|
||||
expect(isAclActive).toBe(true);
|
||||
await expect(page.getByLabel('ACL').getByText('Remove')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('enable & disabled plugin', () => {
|
||||
test('enable plugin', async ({ page, mockPage }) => {
|
||||
await mockPage().goto();
|
||||
await page.getByTestId('plugin-manager-button').click();
|
||||
await page.getByPlaceholder('Search plugin').fill('hello');
|
||||
await expect(page.getByLabel('Hello')).toBeVisible();
|
||||
const isActive = await page.getByLabel('Hello').getByLabel('enable').isChecked();
|
||||
expect(isActive).toBe(false);
|
||||
//激活插件
|
||||
await page.getByLabel('Hello').getByLabel('enable').click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
//等待弹窗消失和页面刷新结束
|
||||
await waitForModalToBeHidden(page);
|
||||
await page.waitForLoadState('load');
|
||||
await page.getByPlaceholder('Search plugin').fill('hello');
|
||||
await expect(await page.getByLabel('Hello').getByLabel('enable').isChecked()).toBe(true);
|
||||
//将激活的插件禁用
|
||||
await page.getByLabel('Hello').getByLabel('enable').click();
|
||||
await page.waitForTimeout(1000); // 等待1秒钟
|
||||
//等待弹窗消失和页面刷新结束
|
||||
await waitForModalToBeHidden(page);
|
||||
await page.waitForLoadState('load');
|
||||
await expect(await page.getByLabel('Hello').getByLabel('enable').isChecked()).toBe(false);
|
||||
});
|
||||
});
|
33
packages/core/client/src/__tests__/e2e/utils.ts
Normal file
33
packages/core/client/src/__tests__/e2e/utils.ts
Normal file
@ -0,0 +1,33 @@
|
||||
//颜色值的近似比较,而不是精确比较
|
||||
function approximateColor(actualColor, expectedColor) {
|
||||
const regex = /rgba?\((\d+), (\d+), (\d+)(?:, ([0-9.]+))?\)/;
|
||||
const actualMatch = actualColor.match(regex);
|
||||
const expectedMatch = expectedColor.match(regex);
|
||||
|
||||
if (actualMatch && expectedMatch) {
|
||||
const tolerance = 2; // 允许的颜色分量误差
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const actualValue = parseInt(actualMatch[i], 10);
|
||||
const expectedValue = parseInt(expectedMatch[i], 10);
|
||||
if (Math.abs(actualValue - expectedValue) > tolerance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function waitForModalToBeHidden(page) {
|
||||
await page.waitForFunction(() => {
|
||||
const modal = document.querySelector('.ant-modal');
|
||||
if (modal) {
|
||||
const computedStyle = window.getComputedStyle(modal);
|
||||
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
|
||||
}
|
||||
return true; // 如果找不到modal,也算作不可见
|
||||
});
|
||||
}
|
||||
|
||||
export { approximateColor, waitForModalToBeHidden };
|
@ -32,7 +32,6 @@ export const ActionModal: ComposedActionDrawer<ModalProps> = observer(
|
||||
|
||||
return (
|
||||
<Modal
|
||||
data-testid="action-modal"
|
||||
width={actualWidth}
|
||||
title={field.title}
|
||||
{...(others as ModalProps)}
|
||||
|
@ -555,7 +555,7 @@ Menu.URL = observer(
|
||||
label: (
|
||||
<SchemaContext.Provider value={schema}>
|
||||
<FieldContext.Provider value={field}>
|
||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false} aria-label={t(field.title)}>
|
||||
<Icon type={icon} />
|
||||
<span
|
||||
style={{
|
||||
@ -601,7 +601,11 @@ Menu.SubMenu = observer(
|
||||
label: (
|
||||
<SchemaContext.Provider value={schema}>
|
||||
<FieldContext.Provider value={field}>
|
||||
<SortableItem className={subMenuDesignerCss} removeParentsIfNoChildren={false}>
|
||||
<SortableItem
|
||||
className={subMenuDesignerCss}
|
||||
removeParentsIfNoChildren={false}
|
||||
aria-label={t(field.title)}
|
||||
>
|
||||
<Icon type={icon} />
|
||||
{t(field.title)}
|
||||
<Designer />
|
||||
|
@ -133,7 +133,7 @@ export const PageTabDesigner = ({ schema }) => {
|
||||
return (
|
||||
<div className={'general-schema-designer'}>
|
||||
<div className={'general-schema-designer-icons'}>
|
||||
<Space size={2} align={'center'}>
|
||||
<Space size={3} align={'center'}>
|
||||
<DragHandler>
|
||||
<DragOutlined style={{ marginRight: 0 }} role="button" aria-label={getAriaLabel('drag-handler', 'tab')} />
|
||||
</DragHandler>
|
||||
|
@ -145,6 +145,7 @@ export const FormButtonLinkageRuleAction = observer(
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Space>
|
||||
<Select
|
||||
data-testid="select-linkage-properties"
|
||||
popupMatchSelectWidth={false}
|
||||
value={operator}
|
||||
options={operators}
|
||||
|
@ -613,12 +613,14 @@ export class Collection<
|
||||
});
|
||||
|
||||
for (const model of models) {
|
||||
await model.sync(syncOptions || {
|
||||
await model.sync(
|
||||
syncOptions || {
|
||||
force: false,
|
||||
alter: {
|
||||
drop: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user