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