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 db2resource from '../../../server/src/middlewares/db2resource';
|
||||||
import { uid } from '@nocobase/utils';
|
import { uid } from '@nocobase/utils';
|
||||||
|
|
||||||
|
|
||||||
interface ActionParams {
|
interface ActionParams {
|
||||||
fields?: string[];
|
fields?: string[];
|
||||||
filter?: any;
|
filter?: any;
|
||||||
@ -117,7 +116,8 @@ export class MockServer extends Koa {
|
|||||||
{
|
{
|
||||||
get(target, method: string, receiver) {
|
get(target, method: string, receiver) {
|
||||||
return (params: ActionParams = {}) => {
|
return (params: ActionParams = {}) => {
|
||||||
let { filterByTk, values = {}, file, ...restParams } = params;
|
let { filterByTk } = params;
|
||||||
|
const { values = {}, file, ...restParams } = params;
|
||||||
if (params.associatedIndex) {
|
if (params.associatedIndex) {
|
||||||
resourceOf = 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 { enableToConfig, expect, test } from '@nocobase/test/client';
|
||||||
|
import { approximateColor } from './utils';
|
||||||
|
|
||||||
test.describe('menu', () => {
|
test.describe('menu page', () => {
|
||||||
test('create new page, then delete', async ({ page }) => {
|
test('create new page, then delete', async ({ page, mockPage }) => {
|
||||||
await page.goto('/');
|
await mockPage().goto();
|
||||||
await enableToConfig(page);
|
const pageTitle = 'new page';
|
||||||
|
await page.getByLabel('schema-initializer-Menu-header').click();
|
||||||
await page.getByLabel('schema-initializer-Menu-header').hover();
|
await page.waitForTimeout(1000); // 等待1秒钟
|
||||||
await page.getByLabel('Page').click();
|
await page.getByRole('menu').getByLabel('Page').click();
|
||||||
await page.getByRole('textbox').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.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.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(pageTitle).click();
|
||||||
await page.getByLabel('designer-schema-settings-Menu.Item').hover();
|
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.getByLabel('Delete').click();
|
||||||
await page.getByRole('button', { name: 'OK' }).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 (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
data-testid="action-modal"
|
|
||||||
width={actualWidth}
|
width={actualWidth}
|
||||||
title={field.title}
|
title={field.title}
|
||||||
{...(others as ModalProps)}
|
{...(others as ModalProps)}
|
||||||
|
@ -555,7 +555,7 @@ Menu.URL = observer(
|
|||||||
label: (
|
label: (
|
||||||
<SchemaContext.Provider value={schema}>
|
<SchemaContext.Provider value={schema}>
|
||||||
<FieldContext.Provider value={field}>
|
<FieldContext.Provider value={field}>
|
||||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
<SortableItem className={designerCss} removeParentsIfNoChildren={false} aria-label={t(field.title)}>
|
||||||
<Icon type={icon} />
|
<Icon type={icon} />
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
@ -601,7 +601,11 @@ Menu.SubMenu = observer(
|
|||||||
label: (
|
label: (
|
||||||
<SchemaContext.Provider value={schema}>
|
<SchemaContext.Provider value={schema}>
|
||||||
<FieldContext.Provider value={field}>
|
<FieldContext.Provider value={field}>
|
||||||
<SortableItem className={subMenuDesignerCss} removeParentsIfNoChildren={false}>
|
<SortableItem
|
||||||
|
className={subMenuDesignerCss}
|
||||||
|
removeParentsIfNoChildren={false}
|
||||||
|
aria-label={t(field.title)}
|
||||||
|
>
|
||||||
<Icon type={icon} />
|
<Icon type={icon} />
|
||||||
{t(field.title)}
|
{t(field.title)}
|
||||||
<Designer />
|
<Designer />
|
||||||
|
@ -133,7 +133,7 @@ export const PageTabDesigner = ({ schema }) => {
|
|||||||
return (
|
return (
|
||||||
<div className={'general-schema-designer'}>
|
<div className={'general-schema-designer'}>
|
||||||
<div className={'general-schema-designer-icons'}>
|
<div className={'general-schema-designer-icons'}>
|
||||||
<Space size={2} align={'center'}>
|
<Space size={3} align={'center'}>
|
||||||
<DragHandler>
|
<DragHandler>
|
||||||
<DragOutlined style={{ marginRight: 0 }} role="button" aria-label={getAriaLabel('drag-handler', 'tab')} />
|
<DragOutlined style={{ marginRight: 0 }} role="button" aria-label={getAriaLabel('drag-handler', 'tab')} />
|
||||||
</DragHandler>
|
</DragHandler>
|
||||||
|
@ -145,6 +145,7 @@ export const FormButtonLinkageRuleAction = observer(
|
|||||||
<div style={{ marginBottom: 8 }}>
|
<div style={{ marginBottom: 8 }}>
|
||||||
<Space>
|
<Space>
|
||||||
<Select
|
<Select
|
||||||
|
data-testid="select-linkage-properties"
|
||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={false}
|
||||||
value={operator}
|
value={operator}
|
||||||
options={operators}
|
options={operators}
|
||||||
|
@ -613,12 +613,14 @@ export class Collection<
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
await model.sync(syncOptions || {
|
await model.sync(
|
||||||
force: false,
|
syncOptions || {
|
||||||
alter: {
|
force: false,
|
||||||
drop: false,
|
alter: {
|
||||||
|
drop: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user