test(e2e): add tests for client (#3144)

* test: add tests for lazy loading of association fields

* refactor: migrate

* test: add tests for page

* test: add tests for page menu

* test: add tests for tabs

* test: add tests for detail block

* test: add tests for list block

* test: add tests for grid card block

* test: add tests for filter collapse block

* test: add tests for markdown block

* test: add tests for table block

* test: add tests for table block

* test: add tests for lazy loading of association fields

* test: add tests for data scope

* test: add tests for filter block

* test: add tests for block template

* test: add tests for drag and sorting

* test: add tests for sorting rules

* test: make testing more stable

* Revert "test: make testing more stable"

This reverts commit 78b7badeb65bfc603fb746efbdb8e242a763ae6a.

* perf: remove enableToConfig

* test: make testing more stable

* test: make testing more stable

* test: delete newly created records to make tests more stable

* fix: fix error when deleting records

* test: make testing more stable

* test: make testing more stable

* test: fix tests

* refactor: optimize file structure

* test: fix tests

* test: fix tests

* refactor: optimize description

* refactor: optimize description

* refactor: use __e2e__ as the root directory for test files

* fix: fix build

* test: make testing more stable
This commit is contained in:
Zeke Zhang 2023-12-13 14:14:33 +08:00 committed by GitHub
parent 35c4b5c55a
commit 3b7c1345cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
146 changed files with 14879 additions and 9584 deletions

View File

@ -60,7 +60,8 @@
"overrides": [ "overrides": [
{ {
"files": [ "files": [
"**/e2e/**/*.{ts,js,tsx,jsx}" "**/e2e/**/*.{ts,js,tsx,jsx}",
"**/__e2e__/**/*.{ts,js,tsx,jsx}"
], ],
"rules": { "rules": {
"@typescript-eslint/no-floating-promises": "error" "@typescript-eslint/no-floating-promises": "error"

View File

@ -1,7 +1,6 @@
import gulp from 'gulp'; import gulp from 'gulp';
import path from 'path';
import fs from 'fs';
import gulpTs from 'gulp-typescript'; import gulpTs from 'gulp-typescript';
import path from 'path';
import { ROOT_PATH } from './constant'; import { ROOT_PATH } from './constant';
export const buildDeclaration = (cwd: string, targetDir: string) => { export const buildDeclaration = (cwd: string, targetDir: string) => {
@ -17,6 +16,7 @@ export const buildDeclaration = (cwd: string, targetDir: string) => {
`!${path.join(srcPath, '**/demos{,/**}')}`, `!${path.join(srcPath, '**/demos{,/**}')}`,
`!${path.join(srcPath, '**/__test__{,/**}')}`, `!${path.join(srcPath, '**/__test__{,/**}')}`,
`!${path.join(srcPath, '**/__tests__{,/**}')}`, `!${path.join(srcPath, '**/__tests__{,/**}')}`,
`!${path.join(srcPath, '**/__e2e__{,/**}')}`,
`!${path.join(srcPath, '**/*.mdx')}`, `!${path.join(srcPath, '**/*.mdx')}`,
`!${path.join(srcPath, '**/*.md')}`, `!${path.join(srcPath, '**/*.md')}`,
`!${path.join(srcPath, '**/*.+(test|e2e|spec).+(js|jsx|ts|tsx)')}`, `!${path.join(srcPath, '**/*.+(test|e2e|spec).+(js|jsx|ts|tsx)')}`,

View File

@ -4,6 +4,7 @@ import path from 'path';
export const globExcludeFiles = [ export const globExcludeFiles = [
'!src/**/__tests__', '!src/**/__tests__',
'!src/**/__test__', '!src/**/__test__',
'!src/**/__e2e__',
'!src/**/demos', '!src/**/demos',
'!src/**/fixtures', '!src/**/fixtures',
'!src/**/*.mdx', '!src/**/*.mdx',

View File

@ -1,338 +0,0 @@
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.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).toBeChecked();
//移除按钮
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: '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('schema-initializer-ActionBar-TableActionInitializers-users').hover();
//添加按钮
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
// 向右挪动鼠标指针,用以关闭下拉列表
await page.mouse.move(300, 0);
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('schema-initializer-ActionBar-TableActionInitializers-users').hover();
//添加按钮
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: '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(
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('schema-initializer-ActionBar-TableActionInitializers-users').hover();
//添加按钮
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('action-Action-Add new-create-users-table').click();
await expect(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.getByRole('menuitem', { name: '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('schema-initializer-ActionBar-TableActionInitializers-users').hover();
//添加按钮
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByLabel('action-Action-Add new-create-users-table').click();
await expect(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.getByRole('menuitem', { name: 'Popup size' }).click();
//默认尺寸为Middle
await expect(page.getByTitle('Popup size').locator('.ant-select-selection-item')).toHaveText('Middle');
//设置为large
await page.getByRole('option', { name: 'Large' }).click();
await page.getByLabel('action-Action-Add new-create-users-table').click();
const drawerItem = 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%
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.getByRole('menuitem', { name: '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('menuitem', { 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('select-filter-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', exact: true }).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;
});
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.getByRole('menuitem', { name: '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('menuitem', { 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('select-filter-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', exact: true }).click();
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;
});
//按钮禁用
expect(linkBtnCursor).toBe('not-allowed');
});
});

View File

@ -1,561 +0,0 @@
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').hover();
await page.getByRole('menuitem', { name: 'table Table' }).click();
await page.getByRole('menuitem', { name: 'Users' }).click();
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
await expect(page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users')).toBeVisible();
await expect(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.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).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(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').hover();
await page
.getByLabel('block-item-CardItem-users-table')
.getByRole('button', { name: 'designer-schema-settings' })
.hover();
await page.getByRole('menuitem', { name: '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', exact: true }).click();
await expect(page.getByLabel('block-item-CardItem-users-table').locator('.ant-card-head')).toBeVisible();
await expect(page.getByLabel('block-item-CardItem-users-table').locator('.ant-card-head')).toHaveText(
'block title',
);
//回显
await page
.getByLabel('block-item-CardItem-users-table')
.getByRole('button', { name: 'designer-schema-settings' })
.hover();
await page.getByRole('menuitem', { name: 'Edit block title' }).click();
const inputValue = await page.getByRole('textbox').inputValue();
expect(inputValue).toBe('block title');
});
});
test.describe('block template', () => {
test('save block template & using 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').hover();
await page
.getByLabel('block-item-CardItem-users-form')
.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-users')
.hover();
await page.getByRole('menuitem', { name: 'Save as block template' }).click();
await page.getByLabel('Save as template').getByRole('textbox').fill('Users_Form');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.getByLabel('block-item-CardItem-users-form').hover();
//保存模板后当前区块为引用区块
const titleTag = await page.getByLabel('block-item-CardItem-users-form').locator('.title-tag').nth(1).innerText();
expect(titleTag).toContain('Reference template');
// using block template
await mockPage({
name: 'block template',
pageSchema: tablePageSchema,
}).goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
//使用复制模板
await page.getByRole('menuitem', { name: 'form Form' }).first().hover();
await page.getByRole('menuitem', { name: 'Users' }).hover();
await page.getByRole('menuitem', { name: 'Duplicate template' }).hover();
await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).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' }).hover();
await page.getByRole('menuitem', { name: 'Reference template' }).hover();
await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).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('action-Action.Link-Edit-update-users-table-0').click();
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-users').click();
await page.getByRole('menuitem', { name: 'form Form' }).hover();
await page.getByRole('menuitem', { name: 'Reference template' }).hover();
await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).click();
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-users').hover();
//修改引用模板
await page.locator('.ant-drawer').getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
await page.getByRole('menuitem', { name: '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(page.getByLabel('block-item-CollectionField-users-form-users.phone')).toBeVisible();
await page.locator('.ant-drawer-mask').click();
//删除模板
await page.getByTestId('plugin-settings-button').click();
await page.getByLabel('ui-schema-storage').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', exact: true }).click();
});
});

View File

@ -1,292 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
'4nd32b2msmb': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
psn8ekavlq6: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'78sh75l4aye': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
x1bef0fr3mp: {
_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: {
dmzuakwhn67: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'FormV2',
'x-component-props': {
useProps: '{{ useFormBlockProps }}',
},
properties: {
grid: {
'x-uid': 'hjzgxol8pmk',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-linkage-rules': [
{
condition: {
$and: [],
},
actions: [
{
targetFields: ['nickname'],
operator: 'value',
value: {
mode: 'express',
value: '{{$nForm.nickname}}',
result: '{{$nForm.nickname}}',
},
},
{
targetFields: ['username'],
operator: 'value',
value: {
mode: 'express',
value: '{{$nForm.phone}}',
result: '{{$nForm.phone}}',
},
},
],
},
],
properties: {
ghhc0yhkzo3: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
cogs086ympz: {
_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': 'uham04eoezy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2skfzuw7qxe',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ejr3cir9ka5',
'x-async': false,
'x-index': 1,
},
ffhaknxhqpx: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
i3ryir3ak3d: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
username: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field': 'users.username',
'x-component-props': {},
'x-uid': 'kw6xnym0d2b',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'l7w26sen1yq',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'agrwgc6u5nh',
'x-async': false,
'x-index': 2,
},
duw19c22p3a: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'64y498coobd': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
email: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field': 'users.email',
'x-component-props': {},
'x-uid': 'yw4htrn4yus',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'co2ntoxojzd',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '1ma4kg0h049',
'x-async': false,
'x-index': 3,
},
gkcvoutlsnn: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'0wpsholkerv': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
phone: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field': 'users.phone',
'x-component-props': {},
'x-uid': 'vuakogudr4a',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'pjbv5opckij',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7r2oycam1jj',
'x-async': false,
'x-index': 4,
},
},
'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': 'q2ln2amp70q',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'ftb89wttlgb',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7xk8np3hom7',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'etlsqr2svs6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'vnqpjz7rukb',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xq6asyjoszu',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'audztrgao6i',
'x-async': true,
'x-index': 1,
},
};
// fix https://nocobase.height.app/T-2165
test('BUG: variable labels should be displayed normally', async ({ page, mockPage }) => {
await mockPage(config).goto();
await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
await expect(page.getByText('Current form / Nickname')).toBeVisible();
await expect(page.getByText('Current form / Phone')).toBeVisible();
});

View File

@ -1,583 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
'x-index': 1,
properties: {
x9ersztdq7x: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-index': 1,
properties: {
ppgwx2drpng: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-index': 1,
properties: {
c25gfj884oe: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-index': 1,
properties: {
urmc26btvb5: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'test2174:list',
'x-decorator-props': {
collection: 'test2174',
resource: 'test2174',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
disableTemplate: false,
},
'x-designer': 'TableBlockDesigner',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-index': 1,
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'TableActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-index': 1,
'x-uid': '7wfu81uqxox',
'x-async': false,
},
yxgybgmfhkp: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'TableColumnInitializers',
'x-component': 'TableV2',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
useProps: '{{ useTableBlockProps }}',
},
'x-index': 2,
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',
'x-index': 1,
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-index': 1,
properties: {
r51kgwrhpgd: {
'x-uid': 'udzm8hggmmb',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'View details',
'x-action': 'view',
'x-designer': 'Action.Designer',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
danger: false,
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
'x-index': 1,
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
'x-index': 1,
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
'x-index': 1,
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-index': 1,
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
'x-index': 1,
properties: {
g8w7wq09bgo: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-index': 1,
properties: {
'42t1ais26x8': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-index': 1,
properties: {
zmq6hmh2i3a: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-acl-action': 'test2174.f_q32e4ieq49n:create',
'x-decorator': 'FormBlockProvider',
'x-decorator-props': {
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
action: null,
resource: 'test2174.f_q32e4ieq49n',
collection: 'test2174',
association: 'test2174.f_q32e4ieq49n',
},
'x-designer': 'FormV2.Designer',
'x-component': 'CardItem',
'x-component-props': {},
'x-index': 1,
properties: {
qii0gw1cb8e: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'FormV2',
'x-component-props': {
useProps: '{{ useFormBlockProps }}',
},
'x-index': 1,
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-index': 1,
properties: {
s3hhb0ohnv1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-index': 1,
properties: {
t2qtgv250s0: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-index': 1,
properties: {
singleSelect: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field':
'test2174.singleSelect',
'x-component-props': {
style: {
width: '100%',
},
},
'x-index': 1,
'x-uid': '75ls3qlq3kw',
'x-async': false,
},
},
'x-uid': 'ndb8zhrxgq0',
'x-async': false,
},
},
'x-uid': '14q94mijyx4',
'x-async': false,
},
},
'x-uid': '6vfyrsrkw29',
'x-async': false,
},
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'CreateFormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
'x-index': 2,
properties: {
afuxokt3osc: {
_isJSONSchemaObject: true,
version: '2.0',
title: '{{ t("Submit") }}',
'x-action': 'submit',
'x-component': 'Action',
'x-designer': 'Action.Designer',
'x-component-props': {
type: 'primary',
htmlType: 'submit',
useProps: '{{ useCreateActionProps }}',
},
'x-action-settings': {
triggerWorkflows: [],
},
type: 'void',
'x-index': 1,
'x-uid': '6idd8u0fs5v',
'x-async': false,
},
},
'x-uid': '8xm66k2wcbq',
'x-async': false,
},
},
'x-uid': 'ia7qdocnj0k',
'x-async': false,
},
},
'x-uid': 'ai78ycshc04',
'x-async': false,
},
},
'x-uid': 'gipnqnxa7c7',
'x-async': false,
},
},
'x-uid': '4fztm3s6pqr',
'x-async': false,
},
},
'x-uid': 'yiwh3chdjzg',
'x-async': false,
},
},
'x-uid': 'mar9ww8zdd6',
'x-async': false,
},
},
'x-uid': '29brl1obftp',
'x-async': false,
},
},
'x-uid': 'r4qbds05tcz',
'x-async': false,
},
},
'x-async': false,
},
},
'x-uid': 'hp7glbaipmc',
'x-async': false,
},
},
'x-uid': 'k9u1fta4jdq',
'x-async': false,
},
x6soi4xw3yn: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-designer': 'TableV2.Column.Designer',
'x-component': 'TableV2.Column',
'x-index': 2,
properties: {
singleSelect: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 'test2174.singleSelect',
'x-component': 'CollectionField',
'x-component-props': {
style: {
width: '100%',
},
ellipsis: true,
},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
default: 'np55dbbny0e',
'x-index': 1,
'x-uid': 'ots1wvv436n',
'x-async': false,
},
},
'x-uid': 'yx4mdriq3jp',
'x-async': false,
},
aoj0myt8kgn: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-designer': 'TableV2.Column.Designer',
'x-component': 'TableV2.Column',
'x-index': 3,
properties: {
f_q32e4ieq49n: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 'test2174.f_q32e4ieq49n',
'x-component': 'CollectionField',
'x-component-props': {
ellipsis: true,
size: 'small',
fieldNames: {
label: 'singleSelect',
value: 'id',
},
},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
'x-index': 1,
properties: {
ony7hpl5247: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View record") }}',
'x-component': 'AssociationField.Viewer',
'x-component-props': {
className: 'nb-action-popup',
},
'x-index': 1,
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
'x-index': 1,
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-index': 1,
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
'x-index': 1,
'x-uid': 'roog7uz0d2o',
'x-async': false,
},
},
'x-uid': 'x05lq9x1n0n',
'x-async': false,
},
},
'x-uid': 'hrymp5hthg3',
'x-async': false,
},
},
'x-uid': 'ujkf9096qwq',
'x-async': false,
},
},
'x-uid': '02kpdjz33yd',
'x-async': false,
},
},
'x-uid': 'e366r5cz2cv',
'x-async': false,
},
},
'x-uid': 'wkdjjugn3l8',
'x-async': false,
},
},
'x-uid': 'n3f07077krt',
'x-async': false,
},
},
'x-uid': 'i920mrykzkp',
'x-async': false,
},
},
'x-uid': '8a5n45kb55h',
'x-async': false,
},
},
'x-uid': 'wcobb00rn5y',
'x-async': false,
},
},
'x-uid': 'pfr9jap0zsf',
'x-async': true,
},
collections: [
{
name: 'test2174',
title: 'Test',
fields: [
{
name: 'f_lkqy3eh4ag7',
interface: 'integer',
isForeignKey: true,
uiSchema: {
type: 'number',
title: 'f_lkqy3eh4ag7',
'x-component': 'InputNumber',
'x-read-pretty': true,
},
},
{
name: 'f_rathx54cqpy',
interface: 'integer',
isForeignKey: true,
uiSchema: {
type: 'number',
title: 'f_rathx54cqpy',
'x-component': 'InputNumber',
'x-read-pretty': true,
},
},
{
name: 'singleSelect',
interface: 'select',
uiSchema: {
enum: [
{
value: 'np55dbbny0e',
label: 'option1',
},
{
value: 'zxteco8rjfc',
label: 'option2',
},
{
value: 'p7pi40zd91q',
label: 'option3',
},
],
type: 'string',
'x-component': 'Select',
title: 'Single select',
},
},
{
name: 'f_q32e4ieq49n',
interface: 'o2m',
foreignKey: 'f_rathx54cqpy',
uiSchema: {
'x-component': 'AssociationField',
'x-component-props': {
multiple: true,
fieldNames: {
label: 'id',
value: 'id',
},
},
title: 'One to many',
},
target: 'test2174',
targetKey: 'id',
sourceKey: 'id',
},
],
},
],
};
// fix https://nocobase.height.app/T-2174
test('BUG: should show default value option', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(config).waitForInit();
await mockRecord('test2174');
await nocoPage.goto();
await page.getByLabel('action-Action.Link-View details-view-test2174-table-0').click();
await page.getByLabel('block-item-CollectionField-test2174-form-test2174.singleSelect-Single select').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-test2174-test2174.singleSelect')
.hover();
await expect(page.getByRole('menuitem', { name: 'Set default value' })).toBeVisible();
});

View File

@ -1,227 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
gyi7sgwfctr: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
nm5sil2dc2y: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'3ux5rf2637w': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
'27osls2fuo8': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'roles:list',
'x-decorator-props': {
collection: 'roles',
resource: 'roles',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'name',
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': 'kk95b2toxch',
'x-async': false,
'x-index': 1,
},
igzxzc2vt2t: {
_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: {
ocawgcp0v8w: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View") }}',
'x-action': 'view',
'x-designer': 'Action.Designer',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View 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("Details")}}',
'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': 'v1z2373u5p1',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'p52m5ti5boo',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2l5qzwu1wg3',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'gkictroi33j',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'a07j11haxbn',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'dqe0do6km5f',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ei7nn2r7iut',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'i9pw6eivnpg',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '13fp2kvtrlv',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '5babsjvcjrr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'uw2uttteuhi',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'kwblwpbedaf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'bzvxogn0d5r',
'x-async': true,
'x-index': 1,
},
};
// fix https://nocobase.height.app/T-2176
test('BUG: Relationship block issue, menu should not display loading', async ({ page, mockPage }) => {
await mockPage(config).goto();
await page.getByLabel('action-Action.Link-View-view-roles-table-root').click();
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-roles').hover();
// 因为下面的断言是判断是否隐藏,又因为一开始就是隐藏的,所以为了准确,需要等待一下
await page.waitForTimeout(300);
// 这个输入框不应该显示
await expect(page.getByPlaceholder('Search and select collection')).toBeHidden();
});

View File

@ -1,178 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
'3c1ivjciciu': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
'1n5r9s23amo': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
bsmuf6ly8b2: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
ppv3te8l7mo: {
_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: {
o8wqytvd6as: {
'x-uid': '9vghmn6u5eo',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Filter") }}',
'x-action': 'filter',
'x-designer': 'Filter.Action.Designer',
'x-component': 'Filter.Action',
'x-component-props': {
icon: 'FilterOutlined',
useProps: '{{ useFilterActionProps }}',
},
'x-align': 'left',
default: {
$and: [],
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'qq7hs80xlut',
'x-async': false,
'x-index': 1,
},
r02zx6qq4ql: {
_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': '4fcw4u63f3p',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'pxqbve61e9i',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'kta6g2v3m9s',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'yu418id0x3t',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xrvoeyz6uop',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'u64df000i8u',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'hofapxvp8p7',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'w7nv1c98j2g',
'x-async': true,
'x-index': 1,
},
};
// fix https://nocobase.height.app/T-2183
test('BUG: should save conditions', async ({ page, mockPage }) => {
await mockPage(config).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID' }).click();
await page.getByRole('button', { name: 'Save conditions' }).click();
await page.reload();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
// After refreshing the browser, the set field and operator should still be visible
await expect(page.getByTestId('select-filter-field').getByText('ID')).toBeVisible();
await expect(page.getByTestId('select-filter-operator').getByText('is')).toBeVisible();
});

View File

@ -1,173 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
cglzmr757wp: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
'0r1mwh6fgdj': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
kjjiumynoor: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
qzbbcmvyy7n: {
_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: {
'8yt1cl9miol': {
'x-uid': 'pcebb2lm84j',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Filter") }}',
'x-action': 'filter',
'x-designer': 'Filter.Action.Designer',
'x-component': 'Filter.Action',
'x-component-props': {
icon: 'FilterOutlined',
useProps: '{{ useFilterActionProps }}',
},
'x-align': 'left',
default: {
$and: [{}],
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'sz9p441he7c',
'x-async': false,
'x-index': 1,
},
fqmmittx9pb: {
_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': 'r4m451vcj6l',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fogxvhca4sg',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '6ywen03do22',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '47n8qnj6q20',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xnc0acxgrxz',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '5dy05u5q2b2',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'rue5m4yfin6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '3xce5ru15d6',
'x-async': true,
'x-index': 1,
},
};
// fix https://nocobase.height.app/T-2186
test('BUG(Filter): the input box displayed should correspond to the field type', async ({ page, mockPage }) => {
await mockPage(config).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
await page.getByTestId('select-filter-field').getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID' }).click();
// 应该显示数字输入框
await expect(page.getByRole('spinbutton')).toBeVisible();
});

View File

@ -1,370 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
n7k4be0tpgt: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
ai6ojrzwkiv: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
amsy67ggjr3: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
ajob701dt18: {
_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': 'farfrcvsydq',
'x-async': false,
'x-index': 1,
},
t1o3f4r1j4q: {
_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: {
ezur2k7gz8k: {
_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',
properties: {
dv88ljcl0qw: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'548w29v01eb': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
q83tzsynghl: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action-props': {
skipScopeCheck: false,
},
'x-acl-action': 'users:update',
'x-decorator': 'FormBlockProvider',
'x-decorator-props': {
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
action: 'get',
resource: 'users',
collection: 'users',
},
'x-designer': 'FormV2.Designer',
'x-component': 'CardItem',
'x-component-props': {},
properties: {
'8kzqujhwsyl': {
_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: {
'3i87cpmv87x': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
am7wls8qu12: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
roles: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field': 'users.roles',
'x-component-props': {},
'x-uid': 'e196r19yspx',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ato3s57xtbt',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'dx3bqu24t50',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'rjbjrnn88sq',
'x-async': false,
'x-index': 1,
},
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'UpdateFormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
'x-uid': 'zh8o2drtd9h',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'ytafkst3w0s',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ioqb8wteiwr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'iavf0r5sl2n',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '994dlhgh1iz',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'k390f3n1rz7',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xm264vyzmkf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'asl2rgfrz7p',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xfre469n0yi',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'gdjgqvxv6b1',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fnctj4uc8xy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2dc4d3mlw3k',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '51i2scjmis1',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '75owqo6visj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'r4bvdbzinup',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'h066j2xouwn',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'qrkik5iy790',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '3vzzy7tipgq',
'x-async': true,
'x-index': 1,
},
};
// fix https://nocobase.height.app/T-2200
test('BUG: should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
await mockPage(config).goto();
await page.getByLabel('action-Action.Link-Edit-update-users-table-0').click();
await expect(page.getByLabel('Admin')).toBeVisible();
await expect(page.getByLabel('Member')).toBeVisible();
await expect(page.getByLabel('Root')).toBeVisible();
await page.getByTestId('select-object-multiple').click();
await page.getByRole('option', { name: 'Member' }).click();
// 再次点击,关闭下拉框。
await page.getByTestId('select-object-multiple').click();
await expect(page.getByLabel('Admin')).toBeVisible();
await expect(page.getByLabel('Member')).toBeHidden();
await expect(page.getByLabel('Root')).toBeVisible();
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.mouse.move(200, 0);
await page.waitForTimeout(200);
await expect(page.getByLabel('Admin')).toBeVisible();
await expect(page.getByLabel('Member')).toBeHidden();
await expect(page.getByLabel('Root')).toBeVisible();
});

View File

@ -1,232 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const config = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
nrwnyusnbrd: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
w85tu6tnlfp: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
q9vljapijwj: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
zia3dyel3jr: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 't_fhdhd0nk7b9:list',
'x-decorator-props': {
collection: 't_fhdhd0nk7b9',
resource: 't_fhdhd0nk7b9',
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: {
'5210td4a1ms': {
_isJSONSchemaObject: true,
version: '2.0',
title: '{{ t("Delete") }}',
'x-action': 'destroy',
'x-component': 'Action',
'x-designer': 'Action.Designer',
'x-decorator': 'ACLActionProvider',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-component-props': {
icon: 'DeleteOutlined',
confirm: {
title: "{{t('Delete record')}}",
content: "{{t('Are you sure you want to delete it?')}}",
},
useProps: '{{ useBulkDestroyActionProps }}',
},
'x-acl-action': 't_fhdhd0nk7b9:destroy',
'x-align': 'right',
type: 'void',
'x-uid': 'aud7zy2m18z',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'j10gefyy50y',
'x-async': false,
'x-index': 1,
},
k88f9zdl4k1: {
_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': 'lky44f6h6kh',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fvkqaxe6bo1',
'x-async': false,
'x-index': 1,
},
wwn0ajwixxl: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-designer': 'TableV2.Column.Designer',
'x-component': 'TableV2.Column',
properties: {
f_vbrlno0zej9: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 't_fhdhd0nk7b9.f_vbrlno0zej9',
'x-component': 'CollectionField',
'x-component-props': {
ellipsis: true,
},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
'x-uid': 'emphc9g52u7',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'q7h24jnc070',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'a9qdzmt4vai',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '98ta7h4ba1f',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'mo6eowotbqd',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2nmkby45kce',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zyukds9t2ln',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'd0j1mfitnmr',
'x-async': true,
'x-index': 1,
},
collections: [
{
name: 't_fhdhd0nk7b9',
title: 'test',
fields: [
{
name: 'f_vbrlno0zej9',
interface: 'input',
},
],
},
],
};
test('check table row', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(config).waitForInit();
const records = await mockRecords('t_fhdhd0nk7b9', 3);
await nocoPage.goto();
await expect(page.getByText(records[0].f_vbrlno0zej9)).toBeVisible();
await page.getByLabel('table-index-1').hover();
await page.getByRole('checkbox', { name: 'checkbox' }).click();
await page.getByLabel('action-Action-Delete-destroy-t_fhdhd0nk7b9-table').click();
// 显示确认弹窗
await expect(page.getByText('Are you sure you want to delete it?')).toBeVisible();
// 点击确认
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 确认内容已被删除
await expect(page.getByText(records[0].f_vbrlno0zej9)).not.toBeVisible();
});

View File

@ -1,264 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const pageConfig = {
collections: [
{
name: 't_wbjenhqk5dz',
title: 'collection1',
inherit: false,
hidden: false,
description: null,
fields: [
{
name: 'id',
type: 'bigInt',
interface: 'id',
description: null,
collectionName: 't_wbjenhqk5dz',
parentKey: null,
reverseKey: null,
autoIncrement: true,
primaryKey: true,
allowNull: false,
uiSchema: {
type: 'number',
title: '{{t("ID")}}',
'x-component': 'InputNumber',
'x-read-pretty': true,
},
},
{
name: 'createdAt',
type: 'date',
interface: 'createdAt',
description: null,
collectionName: 't_wbjenhqk5dz',
parentKey: null,
reverseKey: null,
field: 'createdAt',
uiSchema: {
type: 'datetime',
title: '{{t("Created at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
'x-read-pretty': true,
},
},
{
name: 'createdBy',
type: 'belongsTo',
interface: 'createdBy',
description: null,
collectionName: 't_wbjenhqk5dz',
parentKey: null,
reverseKey: null,
target: 'users',
foreignKey: 'createdById',
uiSchema: {
type: 'object',
title: '{{t("Created by")}}',
'x-component': 'AssociationField',
'x-component-props': {
fieldNames: {
value: 'id',
label: 'nickname',
},
},
'x-read-pretty': true,
},
targetKey: 'id',
},
{
name: 'updatedAt',
type: 'date',
interface: 'updatedAt',
description: null,
collectionName: 't_wbjenhqk5dz',
parentKey: null,
reverseKey: null,
field: 'updatedAt',
uiSchema: {
type: 'string',
title: '{{t("Last updated at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
'x-read-pretty': true,
},
},
{
name: 'updatedBy',
type: 'belongsTo',
interface: 'updatedBy',
description: null,
collectionName: 't_wbjenhqk5dz',
parentKey: null,
reverseKey: null,
target: 'users',
foreignKey: 'updatedById',
uiSchema: {
type: 'object',
title: '{{t("Last updated by")}}',
'x-component': 'AssociationField',
'x-component-props': {
fieldNames: {
value: 'id',
label: 'nickname',
},
},
'x-read-pretty': true,
},
targetKey: 'id',
},
],
category: [],
logging: true,
autoGenId: true,
createdBy: true,
updatedBy: true,
createdAt: true,
updatedAt: true,
sortable: true,
template: 'general',
view: false,
},
{
name: 't_u0c1mmzldgo',
title: 'collection2',
inherit: false,
hidden: false,
description: null,
fields: [
{
name: 'id',
type: 'bigInt',
interface: 'id',
description: null,
collectionName: 't_u0c1mmzldgo',
parentKey: null,
reverseKey: null,
autoIncrement: true,
primaryKey: true,
allowNull: false,
uiSchema: {
type: 'number',
title: '{{t("ID")}}',
'x-component': 'InputNumber',
'x-read-pretty': true,
},
},
{
name: 'createdAt',
type: 'date',
interface: 'createdAt',
description: null,
collectionName: 't_u0c1mmzldgo',
parentKey: null,
reverseKey: null,
field: 'createdAt',
uiSchema: {
type: 'datetime',
title: '{{t("Created at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
'x-read-pretty': true,
},
},
{
name: 'createdBy',
type: 'belongsTo',
interface: 'createdBy',
description: null,
collectionName: 't_u0c1mmzldgo',
parentKey: null,
reverseKey: null,
target: 'users',
foreignKey: 'createdById',
uiSchema: {
type: 'object',
title: '{{t("Created by")}}',
'x-component': 'AssociationField',
'x-component-props': {
fieldNames: {
value: 'id',
label: 'nickname',
},
},
'x-read-pretty': true,
},
targetKey: 'id',
},
{
name: 'updatedAt',
type: 'date',
interface: 'updatedAt',
description: null,
collectionName: 't_u0c1mmzldgo',
parentKey: null,
reverseKey: null,
field: 'updatedAt',
uiSchema: {
type: 'string',
title: '{{t("Last updated at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
'x-read-pretty': true,
},
},
{
name: 'updatedBy',
type: 'belongsTo',
interface: 'updatedBy',
description: null,
collectionName: 't_u0c1mmzldgo',
parentKey: null,
reverseKey: null,
target: 'users',
foreignKey: 'updatedById',
uiSchema: {
type: 'object',
title: '{{t("Last updated by")}}',
'x-component': 'AssociationField',
'x-component-props': {
fieldNames: {
value: 'id',
label: 'nickname',
},
},
'x-read-pretty': true,
},
targetKey: 'id',
},
],
category: [],
logging: true,
autoGenId: true,
createdBy: true,
updatedBy: true,
createdAt: true,
updatedAt: true,
sortable: true,
template: 'general',
view: false,
},
],
};
test.describe('createCollections', () => {
test('quickly create collections', async ({ page, mockPage }) => {
await mockPage(pageConfig).goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('menuitem', { name: 'table Table' }).hover();
await expect(page.getByRole('menuitem', { name: 'collection1' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'collection2' })).toBeVisible();
});
test('no page, just create collections', async ({ page, createCollections }) => {
await createCollections(pageConfig.collections);
await page.goto('/admin/settings/collection-manager/collections');
await expect(page.getByText('collection1')).toBeVisible();
await expect(page.getByText('collection2')).toBeVisible();
});
});

View File

@ -1,54 +0,0 @@
import { expect, test } from '@nocobase/test/client';
test.describe('create data block', () => {
test('table', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('menuitem', { name: 'table Table' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await expect(page.getByText('Configure columns')).toBeVisible();
});
test('form', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('menuitem', { name: 'Form' }).first().hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.mouse.move(300, 0);
await expect(page.getByText('Configure fields')).toBeVisible();
});
test('details', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('menuitem', { name: 'Details' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await expect(page.getByText('Configure fields')).toBeVisible();
});
test('list', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('menuitem', { name: 'ordered-list List' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await expect(page.getByText('Configure fields').first()).toBeVisible();
});
test('grid card', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('menuitem', { name: 'Grid Card' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await expect(page.getByText('Configure fields').first()).toBeVisible();
});
});

View File

@ -1,332 +0,0 @@
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').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Username' }).click();
await page.getByRole('menuitem', { name: '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(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Username' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Email' }).getByRole('switch')).toBeChecked();
//移除字段
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Username' }).click();
await page.getByRole('menuitem', { name: '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();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Username' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Email' }).getByRole('switch')).not.toBeChecked();
});
});
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.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Username' }).click();
await page.getByRole('menuitem', { name: 'Email' }).click();
const sourceElement = page.getByLabel('block-item-CollectionField-users-form-users.nickname');
await sourceElement.hover();
const source = sourceElement.getByLabel('designer-drag');
await source.hover();
const targetElement = page.getByLabel('block-item-CollectionField-users-form-users.username');
await source.dragTo(targetElement);
const targetElement2 = page.getByLabel('block-item-CollectionField-users-form-users.email');
await source.hover();
await source.dragTo(targetElement2);
await sourceElement.hover();
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);
//拖拽调整排序符合预期
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').hover();
await page.getByRole('menuitem', { name: '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').hover();
await page.getByRole('menuitem', { name: 'Edit field title' }).click();
await page.getByLabel('block-item-Input-users-Field title').getByRole('textbox').fill('Username1');
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Edit field title' }).click();
await expect(page.getByLabel('block-item-Input-users-Field title').getByRole('textbox')).toHaveValue('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').hover();
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Display title' }).hover();
//默认显示
await expect(page.getByRole('menuitem', { name: 'Display title' }).getByRole('switch')).toBeChecked();
//设置不显示标题
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Edit description' }).click();
await page.getByLabel('block-item-Input.TextArea-users').locator('textarea').fill(description);
await page.getByRole('button', { name: 'OK', exact: true }).click();
const descriptionItem = 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);
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.getByRole('menuitem', { name: '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')
.hover();
await page.getByRole('menuitem', { name: 'Required' }).click();
await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-users').click();
await page.getByRole('menuitem', { name: 'Submit' }).click();
await page.getByLabel('action-Action-Submit-submit-users-form').click();
//必填校验符合预期
await expect(
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)');
// TODO: 该断言无效,因为在任何情况下该断言都能通过
// 断言表单未被提交
expect(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.getByRole('menuitem', { name: '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')
.hover();
await page.getByRole('menuitem', { name: 'Set validation rules' }).click();
await page.getByRole('button', { name: 'plus Add validation rule' }).click();
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 = page
.getByLabel('block-item-CollectionField-users-form-users.nickname')
.locator('.ant-formily-item-error-help');
await expect(errorInfo).toBeVisible();
//报错信息符合预期
expect(await errorInfo.innerText()).toBe(errorMessage);
await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-users').click();
await page.getByRole('menuitem', { name: 'Submit' }).click();
await page.getByLabel('action-Action-Submit-submit-users-form').click();
// TODO: 该断言无效,因为在任何情况下该断言都能通过
// 断言表单未被提交
expect(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.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByLabel('block-item-CollectionField-users-form-users.nickname').click();
const inputElement = 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')
.hover();
await page.getByRole('menuitem', { name: 'Pattern' }).click();
//禁用
await page.getByRole('option', { name: 'Readonly' }).click();
await expect(inputElement).toBeDisabled();
await page.getByRole('menuitem', { name: 'Pattern' }).click();
//只读
await page.getByText('Easy-reading').click();
await expect(
page.getByLabel('block-item-CollectionField-users-form-users.nickname').locator('.ant-description-input'),
).toBeInViewport();
});
});

View File

@ -1,66 +0,0 @@
import { T2614, T2615, expect, test } from '@nocobase/test/client';
test.describe('load association data', () => {
// https://nocobase.height.app/T-2615
test('should load association data', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2615).waitForInit();
await mockRecord('T2615');
await nocoPage.goto();
// 1. 新增表单中应该显示关系字段的数据
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o').getByLabel('Search').click();
await page.getByRole('option', { name: '1' }).click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o.m2oOfTarget1')).toHaveText(
`m2oOfTarget1:1`,
);
// 关闭弹窗
await page.getByLabel('drawer-Action.Container-T2615-Add record-mask').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 2. 编辑表单中应该显示关系字段的数据
await page.getByLabel('action-Action.Link-Edit record-update-T2615-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o')).toHaveText(`m2o:1`);
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o.m2oOfTarget1')).toHaveText(
`m2oOfTarget1:1`,
);
await page.getByLabel('drawer-Action.Container-T2615-Edit record-mask').click();
// 3. 详情中应该显示关系字段的数据
await page.getByLabel('action-Action.Link-View record-view-T2615-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o')).toHaveText(`m2o:1`);
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o.m2oOfTarget1')).toHaveText(
`m2oOfTarget1:1`,
);
});
// https://nocobase.height.app/T-2614
test('should load association data in subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2614).waitForInit();
await mockRecord('T2614');
await nocoPage.goto();
// 查看详情
await page.getByLabel('action-Action.Link-View record-view-T2614-table-0').click();
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1-m2oOfTarget1'),
).toHaveText(`m2oOfTarget1:1`);
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1.id'),
).toHaveText(`ID:1`);
// 关闭弹窗
await page.getByLabel('drawer-Action.Container-T2614-View record-mask').click();
// 编辑详情
await page.getByLabel('action-Action.Link-Edit record-update-T2614-table-0').click();
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1-m2oOfTarget1'),
).toHaveText(`m2oOfTarget1:1`);
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1.id'),
).toHaveText(`ID:1`);
});
});

View File

@ -1,212 +0,0 @@
import { enableToConfig, expect, test } from '@nocobase/test/client';
import { approximateColor } from './utils';
test.describe('menu page', () => {
test('create new page, then delete', async ({ page, mockPage }) => {
await mockPage().goto();
const pageTitle = 'new page';
await page.getByTestId('schema-initializer-Menu-header').hover();
await page.getByRole('menuitem', { name: 'Page' }).click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill(pageTitle);
await page.getByRole('button', { name: 'OK', exact: true }).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 activeBackgroundColor = await menuItem.evaluate((element) => {
const computedStyle = window.getComputedStyle(element);
return computedStyle.backgroundColor;
});
// 菜单高亮/进入空页面只有一个add block 按钮
expect(activeBackgroundColor).not.toBe(defaultBackgroundColor);
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
await expect(page.getByTitle(pageTitle)).toBeVisible();
const divElement = page.locator('.nb-page-content');
const buttons = await divElement.locator('button').count();
const divText = await divElement.textContent();
expect(buttons).toEqual(1);
expect(divText).toBe('Add block');
// 删除页面,避免影响其他测试
await page.getByLabel(pageTitle).click();
await page.getByLabel(pageTitle).getByLabel('designer-schema-settings').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Edit' }).click();
await page.mouse.move(300, 0);
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill(newPageTitle);
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Move to' }).click();
await page.getByRole('dialog').click();
await page.getByLabel('Search').click();
await page.locator('.ant-select-dropdown').getByText(pageTitle2).click();
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Insert before' }).hover();
await page.getByRole('menuitem', { name: 'Page', exact: true }).click();
await page.getByRole('textbox').fill(pageTitle4);
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Insert after' }).hover();
await page.getByRole('menuitem', { name: 'Page', exact: true }).click();
await page.getByRole('textbox').fill(pageTitle6);
await page.getByRole('button', { name: 'OK', exact: true }).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').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
});
});
test.describe('menu group', () => {
test('create new menu group, then delete', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByTestId('schema-initializer-Menu-header').hover();
await page.getByRole('menuitem', { name: 'Group' }).click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('menu Group');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.getByText('menu Group').click();
await expect(page.getByTestId('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.getByTestId('schema-initializer-Menu-side').click();
await page.getByRole('menuitem', { name: 'Page', exact: true }).click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('group page');
await page.getByRole('button', { name: 'OK', exact: true }).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;
});
const isApproximate = approximateColor(menuItemBackgroundColor, 'rgba(255, 255, 255, 0.1)');
expect(isApproximate).toBe(true);
const pageItem = page.getByRole('menu').locator('li').filter({ hasText: 'group page' });
const pageItemBackgroundColor = await pageItem.evaluate((element) => {
const computedStyle = window.getComputedStyle(element);
return computedStyle.backgroundColor;
});
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.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).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, deletePage }) => {
await mockPage().goto();
await page.getByTestId('schema-initializer-Menu-header').hover();
await page.getByRole('menuitem', { name: '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', exact: true }).click();
await page.getByLabel('link menu').click();
const page2 = await page.waitForEvent('popup');
expect(page2.url()).toBe('https://www.baidu.com/');
// 删除页面,避免影响其他测试
await deletePage('link menu');
});
});

View File

@ -1,196 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const phonePageConfig = {
collections: [
{
name: 't_x3mxc1ymorw',
title: 'mock-testing',
fields: [
{
name: 'email',
title: 'Email',
interface: 'email',
},
],
},
],
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
'x-index': 1,
properties: {
zyfy6q68u10: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-index': 1,
properties: {
nkbrwj149hw: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
t9wwr80ymdc: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
b0iohq28s9u: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 't_x3mxc1ymorw:list',
'x-decorator-props': {
collection: 't_x3mxc1ymorw',
resource: 't_x3mxc1ymorw',
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': '0wksx4dtgum',
'x-async': false,
'x-index': 1,
},
zvooo4ygn4x: {
_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': '3gombpceyjl',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'vcjbrby0i27',
'x-async': false,
'x-index': 1,
},
olrr4chfvfh: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-designer': 'TableV2.Column.Designer',
'x-component': 'TableV2.Column',
properties: {
email: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 't_x3mxc1ymorw.email',
'x-component': 'CollectionField',
'x-component-props': {
ellipsis: true,
},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
'x-uid': 'sqogd3z6o95',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'vcve0j78imw',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'benml5cswa0',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'v0bydleh1ni',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'rmplhh05qpr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'szfwommojdg',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'k8t4d8im9lx',
'x-async': false,
},
},
'x-uid': 't6726o68agh',
'x-async': true,
},
};
test.describe('mock record', () => {
test('email field', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(phonePageConfig).waitForInit();
const records = await mockRecords<{ email: string }>('t_x3mxc1ymorw', 3);
await nocoPage.goto();
await expect(page.getByRole('cell', { name: records[0].email })).toBeVisible();
await expect(page.getByRole('cell', { name: records[1].email })).toBeVisible();
await expect(page.getByRole('cell', { name: records[2].email })).toBeVisible();
});
});

View File

@ -1,178 +0,0 @@
import { 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.getByRole('menuitem', { name: 'Enable page header' }).getByRole('switch')).toBeChecked();
//关闭
await page.getByRole('menuitem', { name: 'Enable page header' }).getByRole('switch').click();
await expect(page.locator('.ant-page-header')).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Enable page header' }).getByRole('switch').click();
await expect(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.getByRole('menuitem', { name: 'Display page title' }).getByRole('switch')).toBeChecked();
//不显示
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Display page title' }).getByRole('switch')).not.toBeChecked();
//开启
await page.locator('.ant-page-header').click();
await page.getByLabel('designer-schema-settings-Page').hover();
await page.getByRole('menuitem', { name: '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.getByRole('menuitem', { name: 'Edit page title' }).click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('page title2');
await page.getByRole('button', { name: 'OK', exact: true }).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.getByRole('menuitem', { name: 'Enable page tabs' }).getByRole('switch')).not.toBeChecked();
//启用标签
await page.getByRole('menuitem', { name: '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', exact: true }).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', exact: true }).click();
await page.getByText('page tab 2').click();
//激活的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(page.getByRole('tab', { name: 'page tab 2' })).toHaveAttribute('aria-selected', 'true');
//修改tab名称
await page.getByText('Unnamed').hover();
await page.getByRole('tab', { name: 'Unnamed' }).getByLabel('designer-schema-settings-Page').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByRole('textbox').fill('page tab');
await page.getByRole('button', { name: 'OK', exact: true }).click();
const tabMenuItem1 = await page.getByRole('tab').getByText('page tab', { exact: true });
await expect(tabMenuItem1).toBeVisible();
await expect(page.getByLabel('schema-initializer-Grid-BlockInitializers')).toBeVisible();
//删除 tab
await page.getByRole('tab').getByText('page tab', { exact: true }).hover();
await page
.getByRole('tab', { name: 'page tab designer-drag-handler' })
.getByLabel('designer-schema-settings')
.hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByRole('tab').getByText('page tab', { exact: true })).not.toBeVisible();
await page.getByRole('tab').getByText('page tab 1').click();
//禁用标签
await page.getByTitle('page tab', { exact: true }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Page' }).hover();
await page.getByRole('menuitem', { name: 'Enable page tabs' }).click();
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 = page.locator('span:has-text("tab 2")');
await sourceElement.hover();
const source = page.getByRole('button', { name: 'drag' });
await source.hover();
const targetElement = 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
expect(tab2.x).toBeLessThan(tab1.x);
});
});

View File

@ -1,462 +0,0 @@
import { expect, test } from '@nocobase/test/client';
const pageConfig = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
pdxwiogf3pc: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
txjj2q9859s: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
w4ziz4txnrn: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
xpfe7mh0n0p: {
'x-uid': '7pdacls95qk',
_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': [],
'x-component-props': {
title: 'Table block',
},
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': 'wb5mvwm537t',
'x-async': false,
'x-index': 1,
},
xj9bu0sv4yz: {
_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': '33rzky3kemq',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7nnw5grzet8',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'm54bsjcv09a',
'x-async': false,
'x-index': 2,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': '4yi4kr1wgkz',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'g6bhfeaba55',
'x-async': false,
'x-index': 1,
},
j9xgdusioku: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'4pdar7yt1d1': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
prtqni04a5t: {
'x-uid': 'coyws8els85',
_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': {
title: 'Form block',
},
properties: {
uo3q3bs1l4c: {
_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': '54w1s4gkrp4',
'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': 'wx1ymybhw4d',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '47x9aotslaf',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': '4tdyzz3hh3s',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '5pr5x41th9y',
'x-async': false,
'x-index': 2,
},
hlm6jtcx5px: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
'0if9syydrxn': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
fhi0k7tswj1: {
'x-uid': 'dntzs4ojq9y',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action': 'users:view',
'x-decorator': 'DetailsBlockProvider',
'x-decorator-props': {
resource: 'users',
collection: 'users',
readPretty: true,
action: 'list',
params: {
pageSize: 1,
},
rowKey: 'id',
},
'x-designer': 'DetailsDesigner',
'x-component': 'CardItem',
'x-component-props': {
title: 'Details block',
},
properties: {
ru4sjmi898d: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Details',
'x-read-pretty': true,
'x-component-props': {
useProps: '{{ useDetailsBlockProps }}',
},
properties: {
'74jvk36v996': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'DetailsActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 24,
},
},
'x-uid': 'zpt2ewuvd3i',
'x-async': false,
'x-index': 1,
},
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'ReadPrettyFormItemInitializers',
'x-uid': 'ivgyowpi1rj',
'x-async': false,
'x-index': 2,
},
pagination: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Pagination',
'x-component-props': {
useProps: '{{ useDetailsPaginationProps }}',
},
'x-uid': 'yl3bc57sqmx',
'x-async': false,
'x-index': 3,
},
},
'x-uid': 't3sshcjbw53',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'so9f6fq7nmf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jdrrxpdel86',
'x-async': false,
'x-index': 3,
},
ipq256mnxif: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
h65018f1z8b: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
ym27n2ko5vr: {
'x-uid': 'j45qq035eas',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action': 'users:view',
'x-decorator': 'List.Decorator',
'x-decorator-props': {
resource: 'users',
collection: 'users',
readPretty: true,
action: 'list',
params: {
pageSize: 10,
},
runWhenParamsChanged: true,
rowKey: 'id',
},
'x-component': 'CardItem',
'x-designer': 'List.Designer',
'x-component-props': {
title: 'List block',
},
properties: {
actionBar: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'ListActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-uid': 'c66vjxpf8bf',
'x-async': false,
'x-index': 1,
},
list: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-component': 'List',
'x-component-props': {
props: '{{ useListBlockProps }}',
},
properties: {
item: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'object',
'x-component': 'List.Item',
'x-read-pretty': true,
'x-component-props': {
useProps: '{{ useListItemProps }}',
},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'ReadPrettyFormItemInitializers',
'x-initializer-props': {
useProps: '{{ useListItemInitializerProps }}',
},
'x-uid': 'uqml2dwp9rv',
'x-async': false,
'x-index': 1,
},
actionBar: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-align': 'left',
'x-initializer': 'ListItemActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
useProps: '{{ useListActionBarProps }}',
layout: 'one-column',
},
'x-uid': 'de6lsblu8nf',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'uzulduo8g1z',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 's53c3p9hmf1',
'x-async': false,
'x-index': 2,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zg9l4uw1rmy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'qw9hle324xg',
'x-async': false,
'x-index': 4,
},
},
'x-uid': 'lvyncy91yyv',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ny9ds6mjkkj',
'x-async': true,
'x-index': 1,
},
};
test.describe('pageSchema', () => {
test('quickly create page schema', async ({ page, mockPage }) => {
await mockPage(pageConfig).goto();
await expect(page.getByText('Table block')).toBeVisible();
await expect(page.getByText('Form block')).toBeVisible();
await expect(page.getByText('Details block')).toBeVisible();
await expect(page.getByText('List block')).toBeVisible();
});
});

View File

@ -1,103 +0,0 @@
import {
expect,
oneDetailBlockWithM2oFieldToGeneral,
oneFormBlockWithRolesFieldBasedUsers,
test,
} from '@nocobase/test/client';
test.describe('association fields with dialog', () => {
test('data selector', async ({ page, mockPage }) => {
await mockPage(oneFormBlockWithRolesFieldBasedUsers).goto();
// open dialog
await page.getByTestId('select-data-picker').click();
// add blocks
await page.getByLabel('schema-initializer-Grid-TableSelectorInitializers-roles').hover();
await page.getByRole('menuitem', { name: 'form Table' }).click();
// 筛选区块这里应该是可以直接点击的,不应该有二级菜单
await page.getByText('Form').click();
await page.getByRole('menuitem', { name: 'Collapse' }).click();
await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-CardItem-roles-table-selector')).toBeVisible();
await expect(page.getByLabel('block-item-CardItem-roles-filter-form')).toBeVisible();
// 向下滚动一点距离,使得下方的区块可见
await page.getByLabel('block-item-CardItem-roles-table-selector').hover();
await page.mouse.wheel(0, 500);
await expect(page.getByLabel('block-item-CardItem-roles-filter-collapse')).toBeVisible();
await expect(page.getByLabel('block-item-Markdown.Void-roles-form')).toBeVisible();
});
test('click association link to view', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneDetailBlockWithM2oFieldToGeneral).waitForInit();
const record = await mockRecord('targetToGeneral');
await nocoPage.goto();
// open dialog
await page
.getByLabel('block-item-CollectionField-targetToGeneral-details-targetToGeneral.toGeneral-toGeneral')
.getByText(record.id, { exact: true })
.click();
// add a tab
await page.getByLabel('schema-initializer-Tabs-TabPaneInitializers-general').click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('test8');
await page.getByLabel('action-Action-Submit-general-details').click();
await expect(page.getByText('test8')).toBeVisible();
// add blocks
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.getByText('Form').click();
await page.getByRole('menuitem', { name: 'Markdown' }).click();
await expect(page.getByText('GeneralConfigure actionsConfigure fields')).toBeVisible();
await expect(page.getByText('GeneralConfigure fieldsConfigure actions')).toBeVisible();
await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible();
// 删除已创建的 blocks腾出页面空间
// delete details block
await page.getByText('GeneralConfigure actionsConfigure fields').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.ReadPrettyDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// delete form block
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// delete markdown block
await page.getByLabel('block-item-Markdown.Void-general-markdown').hover();
await page.getByLabel('designer-schema-settings-Markdown.Void-Markdown.Void.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// add relationship blocks
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Many to one' }).hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'One to many' }).hover();
// 下拉列表中,可选择以下区块进行创建
await expect(page.getByText('Table')).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible();
await expect(page.getByText('Form').nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Calendar' })).toBeVisible();
await page.getByText('Table').click();
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
});
});

View File

@ -1,49 +0,0 @@
import { expect, test } from '@nocobase/test/client';
import { createBlock } from './utils';
test.describe('Page.Grid', () => {
test('create block', async ({ page, mockPage }) => {
await mockPage().goto();
const button = page.getByLabel('schema-initializer-Grid-BlockInitializers');
// add table block
await button.hover();
await createBlock(page, 'Table');
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
// add form block
await button.hover();
await createBlock(page, 'Form');
await expect(page.getByLabel('block-item-CardItem-users-form')).toBeVisible();
// add detail block
await button.hover();
await createBlock(page, 'Details');
await expect(page.getByLabel('block-item-CardItem-users-details')).toBeVisible();
// add list block
await button.hover();
await createBlock(page, 'List');
await expect(page.getByLabel('block-item-CardItem-users-list')).toBeVisible();
// add grid card block
await button.hover();
await createBlock(page, 'Grid Card');
await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible();
// add filter form block
await button.hover();
await createBlock(page, 'Filter form');
await expect(page.getByLabel('block-item-CardItem-users-filter-form')).toBeVisible();
// add collapse block
await button.hover();
await createBlock(page, 'Collapse');
await expect(page.getByLabel('block-item-CardItem-users-filter-collapse')).toBeVisible();
// add markdown block
await button.hover();
await createBlock(page, 'Markdown');
await expect(page.getByLabel('block-item-Markdown.Void-markdown')).toBeVisible();
});
});

View File

@ -1,15 +0,0 @@
import { expect, tabPageEmpty, test } from '@nocobase/test/client';
test.describe('Page.Tabs', () => {
test('enable to create tab', async ({ page, mockPage }) => {
await mockPage(tabPageEmpty).goto();
// add tab page
await page.getByLabel('schema-initializer-Page-tabs').click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('tab1');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('tab1', { exact: true })).toBeVisible();
});
});

View File

@ -1,21 +0,0 @@
import { Page } from '@playwright/test';
export const createBlock = async (page: Page, name: string) => {
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
if (name === 'Form') {
await page.getByText('Form', { exact: true }).first().hover();
} else if (name === 'Filter form') {
await page.getByText('Form', { exact: true }).nth(1).hover();
} else {
await page.getByText(name, { exact: true }).hover();
}
if (name === 'Markdown') {
await page.getByRole('menuitem', { name: 'Markdown' }).click();
} else {
await page.getByRole('menuitem', { name: 'Users' }).click();
}
await page.mouse.move(300, 0);
};

View File

@ -1,38 +0,0 @@
import { Page, oneEmptyTableWithTreeCollection, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('add child', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Add child-create-treeCollection-table-0').hover();
await page.getByLabel('designer-schema-settings-Action.Link-Action.Designer-tree').hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableWithTreeCollection).waitForInit();
await nocoPage.goto();
// 添加一行数据
// TODO: 使用 mockRecord 为 tree 表添加一行数据无效
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-treeCollection').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-Grid-CreateFormBlockInitializers-treeCollection').hover();
await page.getByRole('menuitem', { name: 'form Form' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-ActionBar-CreateFormActionInitializers-treeCollection').hover();
await page.getByRole('menuitem', { name: 'Submit' }).click();
await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'Submit' }).click();
// 添加 add child 按钮
await page.getByRole('button', { name: 'Actions' }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-tree').hover();
await page.getByRole('menuitem', { name: 'Add child' }).click();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -1,86 +0,0 @@
import { Page, expect, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('add new', () => {
const showMenu = async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Open mode', 'Popup size', 'Delete'],
});
});
test('edit button', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
await page.getByRole('menuitem', { name: 'Edit button' }).click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('1234');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByRole('button', { name: '1234' })).toBeVisible();
});
test('open mode', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
// 默认是 drawer
await expect(page.getByRole('menuitem', { name: 'Open mode' }).getByText('Drawer')).toBeVisible();
// 切换为 dialog
await page.getByRole('menuitem', { name: 'Open mode' }).click();
await page.getByRole('option', { name: 'Dialog' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
await expect(page.getByTestId('modal-Action.Container-general-Add record')).toBeVisible();
});
test('popup size', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
// 默认值 middle
await expect(page.getByRole('menuitem', { name: 'Popup size' }).getByText('Middle')).toBeVisible();
// 切换为 small
await page.getByRole('menuitem', { name: 'Popup size' }).click();
await page.getByRole('option', { name: 'Small' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
const drawerWidth =
(await page.getByTestId('drawer-Action.Container-general-Add record').boundingBox())?.width || 0;
expect(drawerWidth).toBeLessThanOrEqual(400);
await page.getByLabel('drawer-Action.Container-general-Add record-mask').click();
// 切换为 large
await showMenu(page);
await page.getByRole('menuitem', { name: 'Popup size' }).click();
await page.getByRole('option', { name: 'Large' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
const drawerWidth2 =
(await page.getByTestId('drawer-Action.Container-general-Add record').boundingBox())?.width || 0;
expect(drawerWidth2).toBeGreaterThanOrEqual(800);
});
test('delete', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByRole('button', { name: 'Add new' })).toBeHidden();
});
});

View File

@ -1,19 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('add record', () => {
const showMenu = async (page: Page) => {
await page.getByRole('button', { name: 'Add record' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -1,17 +0,0 @@
import { oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('bulk delete', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectOptions({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Delete' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
},
enableOptions: ['Edit button', 'Delete'],
});
});
});

View File

@ -1,35 +0,0 @@
import { Page, expect, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('delete', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Delete-destroy-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Delete'],
});
});
test('edit button', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await showMenu(page);
await page.getByRole('menuitem', { name: 'Edit button' }).click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('Delete record');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('action-Action.Link-Delete record-destroy-general-table-0')).toBeVisible();
});
});

View File

@ -1,21 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('duplicate', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Duplicate mode', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -1,21 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('edit', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit-update-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -1,8 +0,0 @@
import { expect } from '@nocobase/test/client';
export async function expectOptions({ showMenu, enableOptions, page }) {
await showMenu();
for (const option of enableOptions) {
await expect(page.getByRole('menuitem', { name: option })).toBeVisible();
}
}

View File

@ -1,30 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('filter', () => {
const showMenu = async (page: Page) => {
await page.getByRole('button', { name: 'Filter' }).hover();
await page.getByLabel('designer-schema-settings-Filter.Action-Filter.Action.Designer-general').hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: [
'Edit button',
'Delete',
'Many to one',
'One to many',
'Single select',
'ID',
'Created at',
'Last updated at',
'Created by',
'Last updated by',
],
});
});
});

View File

@ -1,38 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
const addSomeCustomActions = async (page: Page) => {
// 先删除掉之前的 actions
await page.getByRole('button', { name: 'Actions' }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-general').hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
// 再增加两个自定义的 actions
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByRole('menuitem', { name: 'Update record' }).click();
};
test.describe('popup', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Popup-customize:popup-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await addSomeCustomActions(page);
await showMenu(page);
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -1,17 +0,0 @@
import { oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('refresh', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectOptions({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Refresh' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
},
enableOptions: ['Edit button', 'Delete'],
});
});
});

View File

@ -1,25 +0,0 @@
import { oneEmptyFormWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('save record', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyFormWithActions).goto();
await expectOptions({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Save record' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-users' }).hover();
},
enableOptions: [
'Edit button',
'Linkage rules',
'Assign field values',
'Skip required validation',
'After successful submission',
'Bind workflows',
'Delete',
],
});
});
});

View File

@ -1,17 +0,0 @@
import { oneEmptyFormWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('submit', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyFormWithActions).goto();
await expectOptions({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Submit' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-users' }).hover();
},
enableOptions: ['Edit button', 'Linkage rules', 'Bind workflows', 'Delete'],
});
});
});

View File

@ -1,38 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
const addSomeCustomActions = async (page: Page) => {
// 先删除掉之前的 actions
await page.getByRole('button', { name: 'Actions' }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-general').hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
// 再增加两个自定义的 actions
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByRole('menuitem', { name: 'Update record' }).click();
};
test.describe('update record', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Update record-customize:update-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await addSomeCustomActions(page);
await showMenu(page);
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Assign field values', 'After successful submission', 'Delete'],
});
});
});

View File

@ -1,21 +0,0 @@
import { Page, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
import { expectOptions } from './expectOptions';
test.describe('view', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-View-view-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectOptions({
page,
showMenu: () => showMenu(page),
enableOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -1,286 +0,0 @@
import { Page, expect, oneTableBlockWithActionsAndFormBlocks, test } from '@nocobase/test/client';
const clickOption = async (page: Page, optionName: string) => {
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await page.getByRole('menuitem', { name: optionName }).click();
};
test.describe('SchemaSettings: creating form block', () => {
test('Edit block title', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithActionsAndFormBlocks).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 打开编辑弹窗
await clickOption(page, 'Edit block title');
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('Block title 123');
await page.getByRole('button', { name: 'OK', exact: true }).click();
const runExpect = async () => {
// 设置成功后,显示在区块顶部
await expect(
page.getByLabel('block-item-CardItem-general-form').getByText('Block title 123', { exact: true }),
).toBeVisible();
// 再次打开编辑弹窗时,显示的是上次设置的值
await clickOption(page, 'Edit block title');
await expect(page.getByRole('textbox')).toHaveValue('Block title 123');
};
await runExpect();
// 刷新页面后,显示的应该依然是上次设置的值
await page.reload();
await page.getByRole('button', { name: 'Add new' }).click();
await runExpect();
});
test('Linkage rules', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithActionsAndFormBlocks).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 打开编辑弹窗
await clickOption(page, 'Linkage rules');
await page.getByRole('button', { name: 'Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-property-field').click();
await page.getByText('singleLineText', { exact: true }).click();
await page.getByTestId('select-linkage-action-field').click();
await page.getByRole('option', { name: 'Visible' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
const runExpect = async () => {
await expect(page.getByTestId('select-linkage-property-field').getByText('singleLineText')).toBeVisible();
await expect(page.getByTestId('select-linkage-action-field').getByText('Visible')).toBeVisible();
};
// 再次打开,设置的值应该存在
await clickOption(page, 'Linkage rules');
await runExpect();
// 刷新页面后,设置的值应该依然存在
await page.reload();
await page.getByRole('button', { name: 'Add new' }).click();
await clickOption(page, 'Linkage rules');
await runExpect();
});
test('Form data templates', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithActionsAndFormBlocks).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 打开编辑弹窗
await clickOption(page, 'Form data templates');
await page.getByRole('button', { name: 'Add template' }).click();
await page.getByLabel('block-item-Select-general-Title field').getByTestId('select-single').click();
await page.getByRole('option', { name: 'singleLineText' }).click();
await page.getByRole('button', { name: 'singleLineText (Duplicate)' }).click();
await expect(
page.getByRole('button', { name: 'singleLineText (Duplicate)' }).locator('.ant-tree-checkbox'),
).toHaveClass(/ant-tree-checkbox-checked/);
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 区块顶部应该显示 Data template 选项
await expect(page.getByText('Data template:', { exact: true })).toBeVisible();
// 再次打开,设置的值应该存在
await clickOption(page, 'Form data templates');
await expect(page.getByLabel('block-item-Select-general-Title field').getByTestId('select-single')).toHaveText(
'singleLineText',
);
await expect(
page.getByRole('button', { name: 'singleLineText (Duplicate)' }).locator('.ant-tree-checkbox'),
).toHaveClass(/ant-tree-checkbox-checked/);
// 刷新页面后,设置的值应该还在
await page.reload();
await page.getByRole('button', { name: 'Add new' }).click();
await expect(page.getByText('Data template:', { exact: true })).toBeVisible();
await clickOption(page, 'Form data templates');
await expect(page.getByLabel('block-item-Select-general-Title field').getByTestId('select-single')).toHaveText(
'singleLineText',
);
await expect(
page.getByRole('button', { name: 'singleLineText (Duplicate)' }).locator('.ant-tree-checkbox'),
).toHaveClass(/ant-tree-checkbox-checked/);
});
test('Convert reference to duplicate & Save as block template', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithActionsAndFormBlocks).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 打开编辑弹窗
await clickOption(page, 'Save as block template');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 设置成功后,不再显示 Save as block template 选项,而是显示 Convert reference to duplicate 选项
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).toBeVisible();
// 刷新页面
await page.reload();
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).toBeVisible();
// Convert reference to duplicate
await clickOption(page, 'Convert reference to duplicate');
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).not.toBeVisible();
// 刷新页面
await page.reload();
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).not.toBeVisible();
});
test('Delete', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithActionsAndFormBlocks).goto();
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
// 打开编辑弹窗
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 设置成功后,显示在区块顶部
await expect(page.getByLabel('block-item-CardItem-general-form')).not.toBeVisible();
// 刷新页面后,区块依然是被删除状态
await page.reload();
await page.getByRole('button', { name: 'Add new' }).click();
await expect(page.getByLabel('block-item-CardItem-general-form')).not.toBeVisible();
});
});
test.describe('SchemaSettings: editing list block', () => {
test('Edit block title', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
// 打开编辑弹窗
await clickOption(page, 'Edit block title');
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('Block title 123');
await page.getByRole('button', { name: 'OK', exact: true }).click();
const runExpect = async () => {
// 设置成功后,显示在区块顶部
await expect(
page.getByLabel('block-item-CardItem-general-form').getByText('Block title 123', { exact: true }),
).toBeVisible();
// 再次打开编辑弹窗时,显示的是上次设置的值
await clickOption(page, 'Edit block title');
await expect(page.getByRole('textbox')).toHaveValue('Block title 123');
};
await runExpect();
// 刷新页面后,显示的应该依然是上次设置的值
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await runExpect();
});
test('Linkage rules', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
// 打开编辑弹窗
await clickOption(page, 'Linkage rules');
await page.getByRole('button', { name: 'Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-property-field').click();
await page.getByText('singleLineText', { exact: true }).click();
await page.getByTestId('select-linkage-action-field').click();
await page.getByRole('option', { name: 'Visible' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
const runExpect = async () => {
await expect(page.getByTestId('select-linkage-property-field').getByText('singleLineText')).toBeVisible();
await expect(page.getByTestId('select-linkage-action-field').getByText('Visible')).toBeVisible();
};
// 再次打开,设置的值应该存在
await clickOption(page, 'Linkage rules');
await runExpect();
// 刷新页面后,设置的值应该依然存在
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await clickOption(page, 'Linkage rules');
await runExpect();
});
test('Convert reference to duplicate & Save as block template', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
// 打开编辑弹窗
await clickOption(page, 'Save as block template');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 设置成功后,不再显示 Save as block template 选项,而是显示 Convert reference to duplicate 选项
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).toBeVisible();
// 刷新页面
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).toBeVisible();
// Convert reference to duplicate
await clickOption(page, 'Convert reference to duplicate');
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).not.toBeVisible();
// 刷新页面
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).not.toBeVisible();
});
test('Delete', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
// 打开编辑弹窗
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 设置成功后,显示在区块顶部
await expect(page.getByLabel('block-item-CardItem-general-form')).not.toBeVisible();
// 刷新页面后,区块依然是被删除状态
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await expect(page.getByLabel('block-item-CardItem-general-form')).not.toBeVisible();
});
});

View File

@ -1,94 +0,0 @@
import { expect, oneTableBlockWithAddNewAndViewAndEditAndBasicFields, test } from '@nocobase/test/client';
test.describe('formDataTemplates', () => {
test('basic usage', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
const records = await mockRecords('general', 3);
await nocoPage.goto();
const record = records.find((item) => item.id === 2);
const openDialog = async () => {
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Form data templates' }).click();
};
await page.getByRole('button', { name: 'Add new' }).click();
await openDialog();
await page.getByRole('button', { name: 'plus Add template' }).click();
// 非继承表是不需要显示 Collection 选项的
await expect(page.getByText('Collection:')).toBeHidden();
// 添加一个数据范围条件是ID = 2
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID' }).click();
await page.getByLabel('Form data templates').getByRole('spinbutton').click();
await page.getByLabel('Form data templates').getByRole('spinbutton').fill('2');
// 选择 ID 作为 title field
await page.getByTestId('select-single').click();
await page.getByRole('option', { name: 'ID' }).click();
// 仅选中一个字段
await page.getByRole('button', { name: 'singleLineText (Duplicate)' }).click();
// 保存
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 保存成功后应该显示 Data template 选项
await expect(page.getByText('Data template:')).toBeVisible();
// 选择一个模板
await page.getByTestId('select-form-data-template').click();
await page.getByRole('option', { name: 'Template name 1' }).click();
await page.getByTestId('select-object-undefined').click();
// 因为添加了数据范围,所以只显示一个选项
await expect(page.getByRole('option')).toHaveCount(1);
// 选中数据
await page.getByRole('option', { name: '2' }).click();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox'),
).toHaveValue(record.singleLineText);
// 其它未选中的字段的数据应该是空的
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.integer-integer').getByRole('spinbutton'),
).toBeEmpty();
// 同步表单字段
await openDialog();
await page.getByLabel('action-Action.Link-Sync from form fields-general').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 重新选择一下数据,字段值才会被填充
// TODO: 保存后,数据应该直接被填充上
await page.getByLabel('icon-close-select').click();
await page.getByTestId('select-object-undefined').getByLabel('Search').click();
await page.getByRole('option', { name: '2' }).click();
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.email-email').getByRole('textbox'),
).toHaveValue(record.email);
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.integer-integer').getByRole('spinbutton'),
).toHaveValue(String(record.integer));
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.number-number').getByRole('spinbutton'),
).toHaveValue(String(record.number));
// 隐藏模板选项
await openDialog();
await page.getByLabel('Display data template selector').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('Data template:')).toBeHidden();
});
});

View File

@ -1,225 +0,0 @@
import { expect, oneTableBlockWithAddNewAndViewAndEditAndBasicFields, test } from '@nocobase/test/client';
test.describe('LinkageRules', () => {
test('form: basic usage', async ({ page, mockPage }) => {
const openLinkageRules = async () => {
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
};
await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 设置第一组规则 --------------------------------------------------------------------------
// 打开联动规则弹窗
await page.getByLabel('block-item-CardItem-general-form').hover();
await openLinkageRules();
// 增加一组规则
// 条件singleLineText 字段的值包含 123 时
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').click();
await page.getByRole('menuitemcheckbox', { name: 'singleLineText' }).click();
await page.getByLabel('Linkage rules').locator('input[type="text"]').click();
await page.getByLabel('Linkage rules').locator('input[type="text"]').fill('123');
// action禁用 longText 字段
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-property-field').click();
await page.getByRole('tree').getByText('longText').click();
await page.getByTestId('select-linkage-action-field').click();
await page.getByRole('option', { name: 'Disabled' }).click();
// 保存规则
await page.getByRole('button', { name: 'OK' }).click();
// 验证第一组规则 --------------------------------------------------------------------------
// 初始状态下longText 字段是可编辑的
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeEditable();
// 输入 123longText 字段被禁用
await page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.click();
await page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.fill('123');
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeDisabled();
// 清空输入longText 字段应该恢复成可编辑状态
await page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.clear();
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeEditable();
// 修改第一组规则,使其条件中包含一个变量 --------------------------------------------------------------------------
// 当 singleLineText 字段的值包含 longText 字段的值时,禁用 longText 字段
await openLinkageRules();
await page.getByLabel('variable-button').click();
await page.getByRole('menuitemcheckbox', { name: 'Current form' }).click();
await page.getByRole('menuitemcheckbox', { name: 'longText' }).click();
await page.getByRole('button', { name: 'OK' }).click();
// singleLineText 字段和 longText 字段都为空的情况下longText 字段应该是可编辑的
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeEditable();
// 先为 longText 字段填入 123然后为 singleLineText 字段填入 1longText 字段应该是可编辑的
await page
.getByLabel('block-item-CollectionField-general-form-general.longText-longText')
.getByRole('textbox')
.fill('123');
await page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.fill('1');
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeEditable();
// 改变变量的值:即将 longText 字段的值改为 1longText 字段应该是禁用的
await page
.getByLabel('block-item-CollectionField-general-form-general.longText-longText')
.getByRole('textbox')
.click();
await page.keyboard.press('Backspace');
await page.keyboard.press('Backspace');
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeDisabled();
// 添加第二组规则 -------------------------------------------------------------------------------------------
await openLinkageRules();
// 增加一条规则:当 number 字段的值等于 123 时
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.locator('.ant-collapse-header').nth(1).getByRole('img', { name: 'right' }).click();
await page.getByLabel('Linkage rules').getByRole('tabpanel').getByText('Add condition', { exact: true }).click();
await page.getByRole('button', { name: 'Search Select field' }).click();
await page.getByRole('menuitemcheckbox', { name: 'number' }).click();
await page.getByLabel('Linkage rules').getByRole('spinbutton').click();
await page.getByLabel('Linkage rules').getByRole('spinbutton').fill('123');
// action使 longText 字段可编辑
await page.getByLabel('Linkage rules').getByRole('tabpanel').getByText('Add property').click();
await page.getByRole('button', { name: 'Search Select field' }).click();
await page.getByRole('tree').getByText('longText').click();
await page.getByRole('button', { name: 'Search action' }).click();
await page.getByRole('option', { name: 'Editable' }).click();
// action: 为 longText 字段赋上常量值
await page.getByLabel('Linkage rules').getByRole('tabpanel').getByText('Add property').click();
await page.getByRole('button', { name: 'Search Select field' }).click();
await page.getByRole('tree').getByText('longText').click();
await page.getByRole('button', { name: 'Search action' }).click();
await page.getByRole('option', { name: 'Value', exact: true }).click();
await page.getByLabel('dynamic-component-linkage-rules').getByRole('textbox').fill('456');
// action: 为 integer 字段附上一个表达式,使其值等于 number 字段的值
await page.getByLabel('Linkage rules').getByRole('tabpanel').getByText('Add property').click();
await page.getByRole('button', { name: 'Search Select field' }).click();
await page.getByRole('tree').getByText('integer').click();
await page.getByRole('button', { name: 'Search action' }).click();
await page.getByRole('option', { name: 'Value', exact: true }).click();
await page.getByTestId('select-linkage-value-type').nth(1).click();
await page.getByText('Expression').click();
await page.getByText('xSelect a variable').click();
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
await page.getByRole('menuitemcheckbox', { name: 'number' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 验证第二组规则 -------------------------------------------------------------------------------------------
// 此时 longText 字段是禁用状态当满足第二组规则时longText 字段应该是可编辑的
await page
.getByLabel('block-item-CollectionField-general-form-general.number-number')
.getByRole('spinbutton')
.fill('123');
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toBeEditable();
// 并且 longText 字段的值应该是 456
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toHaveValue('456');
// integer 字段的值应该等于 number 字段的值
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.integer-integer').getByRole('spinbutton'),
).toHaveValue('123');
});
test('action: basic usage', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
const record = await mockRecord('general');
await nocoPage.goto();
const openLinkageRules = async () => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
};
// 设置第一组规则 --------------------------------------------------------------------------
await openLinkageRules();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
// 添加一个条件ID 等于 1
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').click();
await page.getByRole('menuitemcheckbox', { name: 'ID' }).click();
await page.getByRole('spinbutton').click();
await page.getByRole('spinbutton').fill('1');
// action: 禁用按钮
await page.getByText('Add property').click();
await page.getByLabel('block-item-ArrayCollapse-general').click();
await page.getByTestId('select-linkage-properties').getByLabel('Search').click();
await page.getByRole('option', { name: 'Disabled' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('action-Action.Link-View record-view-general-table-0')).toHaveAttribute(
'disabled',
'',
);
// 设置第二组规则 --------------------------------------------------------------------------
await openLinkageRules();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.locator('.ant-collapse-header').nth(1).getByRole('img', { name: 'right' }).click();
// 添加一个条件ID 等于 1
await page.getByRole('tabpanel').getByText('Add condition', { exact: true }).click();
await page.getByRole('button', { name: 'Search Select field' }).getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID' }).click();
await page.getByRole('spinbutton').click();
await page.getByRole('spinbutton').fill('1');
// action: 使按钮可用
await page.getByRole('tabpanel').getByText('Add property').click();
await page.getByRole('combobox', { name: 'Search' }).click();
await page.getByRole('option', { name: 'Enabled' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 后面的 action 会覆盖前面的
await expect(page.getByLabel('action-Action.Link-View record-view-general-table-0')).not.toHaveAttribute(
'disabled',
'',
);
});
});

View File

@ -1,204 +0,0 @@
import {
expect,
oneTableBlockWithAddNewAndViewAndEditAndBasicFields,
oneTableBlockWithAddNewAndViewAndEditAndRelationFields,
test,
} from '@nocobase/test/client';
test.describe('set default value', () => {
test('basic fields', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).goto();
const openDialog = async (fieldName: string) => {
await page.getByLabel(`block-item-CollectionField-general-form-general.${fieldName}-${fieldName}`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.${fieldName}`)
.hover();
await page.getByRole('menuitem', { name: 'Set default value', exact: true }).click();
};
await page.getByRole('button', { name: 'Add new' }).click();
await openDialog('singleLineText');
await page.getByLabel('Set default value').getByRole('textbox').click();
await page.getByLabel('Set default value').getByRole('textbox').fill('test default value');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 关闭弹窗在打开,应该显示默认值
await page.getByLabel('drawer-Action.Container-general-Add record-mask').click();
await page.getByRole('button', { name: 'Add new' }).click();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox'),
).toHaveValue('test default value');
// 为 longText 设置一个变量默认值: {{ currentForm.singleLineText }}
await openDialog('longText');
await page.getByLabel('variable-button').click();
await page.getByRole('menuitemcheckbox', { name: 'Current form' }).click();
await page.getByRole('menuitemcheckbox', { name: 'singleLineText' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.getByLabel('drawer-Action.Container-general-Add record-mask').click();
await page.getByRole('button', { name: 'Add new' }).click();
// 值应该和 singleLineText 一致
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toHaveValue('test default value');
// 更改变量的值longText 的值也应该跟着变化
await page
.getByLabel('block-item-CollectionField-general-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.fill('new value');
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.longText-longText').getByRole('textbox'),
).toHaveValue('new value');
});
test('subform: basic fields', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithAddNewAndViewAndEditAndRelationFields).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 先切换为子表单
await page.getByLabel('block-item-CollectionField-general-form-general.manyToOne-manyToOne').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToOne')
.hover();
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-form', exact: true }).click();
// 关闭下拉菜单
await page.getByLabel('block-item-CollectionField-general-form-general.manyToOne-manyToOne').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToOne')
.hover();
await page.mouse.move(100, 0);
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Username' }).click();
// 子表单状态下,没有默认值选项
await page.getByLabel('block-item-CollectionField-general-form-general.manyToOne-manyToOne').hover();
await expect(page.getByRole('menuitem', { name: 'Set default value' })).not.toBeVisible();
// 测试子表单字段默认值 ------------------------------------------------------------------------------------------
await page.getByLabel(`block-item-CollectionField-users-form-users.nickname-Nickname`).hover();
await page.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-users-users.Nickname`).hover();
await page.getByRole('menuitem', { name: 'Set default value', exact: true }).click();
await page.getByLabel('Set default value').getByRole('textbox').click();
await page.getByLabel('Set default value').getByRole('textbox').fill('test default value');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 关闭弹窗在打开,应该显示默认值
await page.getByLabel('drawer-Action.Container-general-Add record-mask').click();
await page.getByRole('button', { name: 'Add new' }).click();
await expect(
page.getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname').getByRole('textbox'),
).toHaveValue('test default value');
// 为 username 设置一个变量默认值: {{ currentObject.nickname }}
await page.getByLabel(`block-item-CollectionField-users-form-users.username-Username`).hover();
await page.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-users-users.Username`).hover();
await page.getByRole('menuitem', { name: 'Set default value', exact: true }).click();
await page.getByLabel('variable-button').click();
await page.getByRole('menuitemcheckbox', { name: 'Current object' }).click();
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.getByLabel('drawer-Action.Container-general-Add record-mask').click();
await page.getByRole('button', { name: 'Add new' }).click();
// 值应该和 Nickname 一致
await expect(
page.getByLabel('block-item-CollectionField-users-form-users.username-Username').getByRole('textbox'),
).toHaveValue('test default value');
// 更改变量的值longText 的值也应该跟着变化
await page
.getByLabel('block-item-CollectionField-users-form-users.nickname-Nickname')
.getByRole('textbox')
.fill('new value');
await expect(
page.getByLabel('block-item-CollectionField-users-form-users.username-Username').getByRole('textbox'),
).toHaveValue('new value');
});
test('subtable: basic fields', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithAddNewAndViewAndEditAndRelationFields).goto();
await page.getByRole('button', { name: 'Add new' }).click();
// 先切换为子表格
await page.getByLabel('block-item-CollectionField-general-form-general.oneToMany-oneToMany').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-general-general.oneToMany')
.hover();
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-table' }).click();
// 关闭下拉菜单
await page.getByLabel('block-item-CollectionField-general-form-general.oneToMany-oneToMany').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-general-general.oneToMany')
.hover();
await page.mouse.move(100, 0);
await page.getByLabel('schema-initializer-AssociationField.SubTable-TableColumnInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Username' }).click();
// 子表格状态下,没有默认值选项
await page.getByLabel('block-item-CollectionField-general-form-general.oneToMany-oneToMany').hover();
await expect(page.getByRole('menuitem', { name: 'Set default value' })).not.toBeVisible();
// 测试子表格字段默认值 ------------------------------------------------------------------------------------------
await page.getByRole('button', { name: 'Nickname', exact: true }).hover();
await page
.getByRole('button', { name: 'designer-schema-settings-TableV2.Column-TableV2.Column.Designer-users' })
.hover();
await page.getByRole('menuitem', { name: 'Set default value', exact: true }).click();
await page.getByLabel('Set default value').getByRole('textbox').click();
await page.getByLabel('Set default value').getByRole('textbox').fill('test default value');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 当新增一行时,应该显示默认值
await page.getByRole('button', { name: 'plus' }).click();
await expect(
page
.getByRole('cell', { name: 'block-item-CollectionField-users-form-users.nickname-Nickname' })
.getByRole('textbox'),
).toHaveValue('test default value');
// 为 username 设置一个变量默认值: {{ currentObject.nickname }}
await page.getByRole('button', { name: 'Username', exact: true }).hover();
await page
.getByRole('button', { name: 'designer-schema-settings-TableV2.Column-TableV2.Column.Designer-users' })
.hover();
await page.getByRole('menuitem', { name: 'Set default value', exact: true }).click();
await page.mouse.move(300, 0);
await page.getByLabel('variable-button').click();
await page.getByRole('menuitemcheckbox', { name: 'Current object' }).click();
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 值应该和 Nickname 一致
await expect(
page
.getByRole('cell', { name: 'block-item-CollectionField-users-form-users.username-Username' })
.getByRole('textbox'),
).toHaveValue('test default value');
// 更改变量的值longText 的值也应该跟着变化
await page
.getByRole('cell', { name: 'block-item-CollectionField-users-form-users.username-Username' })
.getByRole('textbox')
.fill('new value');
await expect(
page
.getByRole('cell', { name: 'block-item-CollectionField-users-form-users.username-Username' })
.getByRole('textbox'),
).toHaveValue('new value');
});
});

View File

@ -1,531 +0,0 @@
import { PageConfig, generalWithMultiLevelRelationshipFields } from '@nocobase/test/client';
export const testingLazyLoadingOfAssociationFieldsForTheCurrentRecord: PageConfig = {
collections: generalWithMultiLevelRelationshipFields,
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
twh6vbpbeh7: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
properties: {
i7hhy9qt3vm: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
zo5ry8mb8i8: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
md9lzbo43sa: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'general:list',
'x-decorator-props': {
collection: 'general',
resource: 'general',
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: {
ztk6e5lq2lu: {
_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',
properties: {
ec8k6t8hyfd: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
i0kq9r8uagp: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
kiwjcmb22ni: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-acl-action': 'general:create',
'x-decorator': 'FormBlockProvider',
'x-decorator-props': {
resource: 'general',
collection: 'general',
},
'x-designer': 'FormV2.Designer',
'x-component': 'CardItem',
'x-component-props': {},
properties: {
q93o4rn1hum: {
_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: {
'11k61q066eh': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
ilk6g3zfb99: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
m2oField0: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field': 'general.m2oField0',
'x-component-props': {},
'x-uid': 'zrdwygwt68n',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jbzdmkicrbq',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '19tec55bhv8',
'x-async': false,
'x-index': 1,
},
t964dpznx8s: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
properties: {
wcc2gxtezpj: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
properties: {
singleLineText: {
'x-uid': 'h6nhc1du9zi',
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-designer': 'FormItem.Designer',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field':
'general.singleLineText',
'x-component-props': {},
default:
'{{$nForm.m2oField0.m2oField1.id}}',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 't1fh9mrbi65',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '5pzmjkt6nk9',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'i969ieh0rfu',
'x-async': false,
'x-index': 1,
},
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'CreateFormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
'x-uid': 'lrwiyzyg7ow',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'gz3ygly62js',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'q8gn2pt12ng',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'p0gdtennahr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'f8741pdnbcy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'f27ge9qf7u6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xa5ujnlaqd6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'd6dy86bjt0b',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'aycw09vymqy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'tzdqfr6lmjy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7zzwexd3e4i',
'x-async': false,
'x-index': 1,
},
qoq998l2i65: {
_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: {
'8kaku37mdre': {
'x-uid': 'j1n2bte2wn4',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'View record',
'x-action': 'view',
'x-designer': 'Action.Designer',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
danger: false,
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View 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("Details")}}',
'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': '6k8q5pvnbjg',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'txzczhz6s4g',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'chdb0wf0riz',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7jpie07p1s7',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
'0xml7nfy4gs': {
'x-uid': 'inv9i7a8i9t',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'Edit record',
'x-action': 'update',
'x-designer': 'Action.Designer',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
danger: false,
},
'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': 'yqsyvw7utn4',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'pb3iqfcd6a5',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '3byo77j4fh6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '1gscyc9hw6f',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'v4zy14lc4g4',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 's2ej5h10jxd',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xoknlt7jnhh',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'ovphvqtknwh',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'y4mqu08ojko',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'mszhp81fvd9',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ylndxgnumz2',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'sj1cs0vneuv',
'x-async': true,
'x-index': 1,
},
};

View File

@ -1,33 +0,0 @@
//颜色值的近似比较,而不是精确比较
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 };

View File

@ -1,7 +1,19 @@
import { expect, oneEmptyFilterCollapseBlock, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyFilterCollapseBlock, test } from '@nocobase/test/client';
test.describe('Filter Collapse', () => { test.describe('where collapse block can be added', () => {
test('add fields', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Collapse');
await expect(page.getByLabel('block-item-CardItem-users-filter-collapse')).toBeVisible();
});
test('data selector popup', async ({ page, mockPage }) => {});
});
test.describe('configure fields', () => {
test('association fields & choices fields', async ({ page, mockPage }) => {
await mockPage(oneEmptyFilterCollapseBlock).goto(); await mockPage(oneEmptyFilterCollapseBlock).goto();
// add fields // add fields

View File

@ -0,0 +1,63 @@
import {
expect,
expectSettingsMenu,
oneCollapseAndOneTableWithSameCollection,
oneEmptyFilterCollapseBlock,
test,
} from '@nocobase/test/client';
test.describe('collapse schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyFilterCollapseBlock).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CardItem-general-filter-collapse').hover();
await page.getByLabel('designer-schema-settings-CardItem-AssociationFilter.BlockDesigner-general').hover();
},
supportedOptions: ['Edit block title', 'Save as template', 'Connect data blocks', 'Delete'],
});
});
test.describe('connect data blocks', () => {
test('connecting two blocks of the same collection', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneCollapseAndOneTableWithSameCollection).waitForInit();
const records = await mockRecords('general', [
{ singleSelect: 'option1' },
{ singleSelect: 'option2' },
{ singleSelect: 'option3' },
]);
await nocoPage.goto();
// 将上面的 Collapse 连接到下面的 Table
await page.getByLabel('block-item-CardItem-general-filter-collapse').hover();
await page.getByLabel('designer-schema-settings-CardItem-AssociationFilter.BlockDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Connect data blocks' }).hover();
await page.getByRole('menuitem', { name: 'General' }).click();
// 点击一个选项,进行筛选
await page.getByRole('button', { name: 'right singleSelect search' }).click();
await page.getByLabel('block-item-CardItem-general-filter-collapse').getByText('Option1').click();
// 注意:在本地运行时,由于运行结束后不会清空之前创建的数据,所以在第一次运行之后,下面会报错。如果遇到这种情况,可以先不管
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: 'Option2' }),
).toBeHidden();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: 'Option3' }),
).toBeHidden();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: 'Option1' }),
).toBeVisible();
// 再次点击,重置筛选
await page.getByLabel('block-item-CardItem-general-filter-collapse').getByText('Option1').click();
await expect(page.getByLabel('block-item-CardItem-general-table').getByRole('row')).toHaveCount(
records.length + 1,
);
});
});
});
test.describe('fields schema settings', () => {});

View File

@ -1,15 +1,24 @@
import { expect, oneEmptyDetailsBlock, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyDetailsBlock, test } from '@nocobase/test/client';
test.describe('Details block', () => { test.describe('where multi data details block can be added', () => {
test('add fields', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Details');
await expect(page.getByLabel('block-item-CardItem-users-details')).toBeVisible();
});
});
test.describe('configure fields', () => {
test('display collection fields & display association fields & add text', async ({ page, mockPage }) => {
await mockPage(oneEmptyDetailsBlock).goto(); await mockPage(oneEmptyDetailsBlock).goto();
const formItemInitializer = page.getByLabel('schema-initializer-Grid-ReadPrettyFormItemInitializers-general'); const formItemInitializer = page.getByLabel('schema-initializer-Grid-ReadPrettyFormItemInitializers-general');
// add fields // add fields
await formItemInitializer.hover(); await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).toBeChecked();
// add association fields // add association fields
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover(); await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
@ -26,8 +35,8 @@ test.describe('Details block', () => {
// delete fields // delete fields
await formItemInitializer.hover(); await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).not.toBeChecked();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover(); await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click(); await page.getByRole('menuitem', { name: 'Nickname' }).click();
@ -46,8 +55,10 @@ test.describe('Details block', () => {
await page.getByRole('menuitem', { name: 'Add text' }).click(); await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-Markdown.Void-general-details')).toBeVisible(); await expect(page.getByLabel('block-item-Markdown.Void-general-details')).toBeVisible();
}); });
});
test('add action buttons', async ({ page, mockPage }) => { test.describe('configure actions', () => {
test('edit & delete & duplicate', async ({ page, mockPage }) => {
await mockPage(oneEmptyDetailsBlock).goto(); await mockPage(oneEmptyDetailsBlock).goto();
await page.getByLabel('schema-initializer-ActionBar-DetailsActionInitializers-general').hover(); await page.getByLabel('schema-initializer-ActionBar-DetailsActionInitializers-general').hover();

View File

@ -0,0 +1,71 @@
import {
expectSettingsMenu,
oneDetailBlockWithM2oFieldToGeneral,
oneEmptyDetailsBlock,
test,
} from '@nocobase/test/client';
test.describe('multi data details block schema settings', () => {
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneDetailBlockWithM2oFieldToGeneral).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CardItem-targetToGeneral-details').hover();
await page.getByLabel('designer-schema-settings-CardItem-DetailsDesigner-targetToGeneral').hover();
},
supportedOptions: [
'Edit block title',
'Set the data scope',
'Set default sorting rules',
'Save as block template',
'Delete',
],
});
});
});
test.describe('actions schema settings', () => {
test('edit & delete & duplicate', async ({ page, mockPage }) => {
await mockPage(oneEmptyDetailsBlock).goto();
// 创建 Edit & Delete & Duplicate 三个按钮
await page.getByLabel('schema-initializer-ActionBar-DetailsActionInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
// Edit button settings ---------------------------------------------------------------
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Edit' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
// Delete settings -------------------------------------------------------------------
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Delete' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Delete'],
});
// Duplicate settings ----------------------------------------------------------------
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Duplicate' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action' }).hover();
},
supportedOptions: ['Edit button', 'Duplicate mode', 'Open mode', 'Popup size', 'Delete'],
});
});
});

View File

@ -0,0 +1,87 @@
import { Page, expect, expectSettingsMenu, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
test.describe('where single data details block can be added', () => {
test('popup', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByLabel('action-Action.Link-View-view-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-general-form')).toBeVisible();
});
});
test.describe('configure actions', () => {
test('edit & delete & duplicate & print & customize', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByLabel('action-Action.Link-View-view-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.mouse.move(300, 0);
// create edit ------------------------------------------------------------------------------------
await createAction(page, 'Edit');
await expect(page.getByLabel('action-Action-Edit-update-general-form-0')).toBeVisible();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action-Edit-update-general-form-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
await deleteAction(page, 'action-Action-Edit-update-general-form-0');
// create delete ------------------------------------------------------------------------------------
await createAction(page, 'Delete');
await expect(page.getByLabel('action-Action-Delete-destroy-general-form-0')).toBeVisible();
// create duplicate ------------------------------------------------------------------------------------
await createAction(page, 'Duplicate');
await expect(page.getByLabel('action-Action-Duplicate-duplicate-general-form-0')).toBeVisible();
// create print
await createAction(page, 'Print');
await expect(page.getByLabel('action-Action-Print-print-general-form-0')).toBeVisible();
// create customize actions ----------------------------------------------------------------------------
// Popup
await createCustomAction(page, 'Popup');
await expect(page.getByLabel('action-Action-Popup-customize:popup-general-form-0')).toBeVisible();
// Update record
await createCustomAction(page, 'Update record');
await expect(page.getByLabel('action-Action-Update record-customize:update-general-form-0')).toBeVisible();
});
});
test.describe('configure fields', () => {});
async function createAction(page: Page, name: string) {
await page.getByLabel('schema-initializer-ActionBar-ReadPrettyFormActionInitializers-general').hover();
await page.getByRole('menuitem', { name: name }).click();
await expect(page.getByRole('menuitem', { name: name }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
}
async function createCustomAction(page: Page, name: string) {
await page.getByLabel('schema-initializer-ActionBar-ReadPrettyFormActionInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: name }).click();
await page.mouse.move(0, 400);
}
async function deleteAction(page: Page, label: string) {
await page.getByLabel(label).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-' }).hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(0, 300);
await page.getByRole('button', { name: 'OK', exact: true }).click();
}

View File

@ -0,0 +1,25 @@
import { expectSettingsMenu, oneTableBlockWithAddNewAndViewAndEditAndBasicFields, test } from '@nocobase/test/client';
test.describe('single details block schema settings', () => {
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
await mockRecord('general');
await nocoPage.goto();
// 打开抽屉
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.ReadPrettyDesigner-general').hover();
},
supportedOptions: ['Edit block title', 'Save as block template', 'Delete'],
});
});
});
test.describe('actions schema settings', () => {});
test.describe('fields schema settings', () => {});

View File

@ -1,11 +1,13 @@
import { import {
expect, expect,
oneDetailBlockWithM2oFieldToGeneral,
oneEmptyTableBlockWithActions, oneEmptyTableBlockWithActions,
oneEmptyTableBlockWithCustomizeActions, oneEmptyTableBlockWithCustomizeActions,
oneFormBlockWithRolesFieldBasedUsers,
test, test,
} from '@nocobase/test/client'; } from '@nocobase/test/client';
test.describe('Actions with dialog', () => { test.describe('where to open a popup and what can be added to it', () => {
test('add new', async ({ page, mockPage }) => { test('add new', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableBlockWithActions).goto(); await mockPage(oneEmptyTableBlockWithActions).goto();
@ -53,7 +55,7 @@ test.describe('Actions with dialog', () => {
await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible(); await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible();
}); });
test('view & edit & popup', async ({ page, mockPage, mockRecord }) => { test('view', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit(); const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general'); await mockRecord('general');
await nocoPage.goto(); await nocoPage.goto();
@ -138,4 +140,91 @@ test.describe('Actions with dialog', () => {
await expect(page.getByLabel('block-item-CardItem-general-form')).toBeVisible(); await expect(page.getByLabel('block-item-CardItem-general-form')).toBeVisible();
await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible(); await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible();
}); });
test('association link', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneDetailBlockWithM2oFieldToGeneral).waitForInit();
const record = await mockRecord('targetToGeneral');
await nocoPage.goto();
// open dialog
await page
.getByLabel('block-item-CollectionField-targetToGeneral-details-targetToGeneral.toGeneral-toGeneral')
.getByText(record.id, { exact: true })
.click();
// add a tab
await page.getByLabel('schema-initializer-Tabs-TabPaneInitializers-general').click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('test8');
await page.getByLabel('action-Action-Submit-general-details').click();
await expect(page.getByText('test8')).toBeVisible();
// add blocks
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.getByText('Form').click();
await page.getByRole('menuitem', { name: 'Markdown' }).click();
await expect(page.getByText('GeneralConfigure actionsConfigure fields')).toBeVisible();
await expect(page.getByText('GeneralConfigure fieldsConfigure actions')).toBeVisible();
await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible();
// 删除已创建的 blocks腾出页面空间
// delete details block
await page.getByText('GeneralConfigure actionsConfigure fields').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.ReadPrettyDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// delete form block
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// delete markdown block
await page.getByLabel('block-item-Markdown.Void-general-markdown').hover();
await page.getByLabel('designer-schema-settings-Markdown.Void-Markdown.Void.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// add relationship blocks
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Many to one' }).hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByRole('menuitem', { name: 'One to many' }).hover();
// 下拉列表中,可选择以下区块进行创建
await expect(page.getByText('Table')).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible();
await expect(page.getByText('Form').nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Calendar' })).toBeVisible();
await page.getByText('Table').click();
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
});
test('data picker', async ({ page, mockPage }) => {
await mockPage(oneFormBlockWithRolesFieldBasedUsers).goto();
// open dialog
await page.getByTestId('select-data-picker').click();
// add blocks
await page.getByLabel('schema-initializer-Grid-TableSelectorInitializers-roles').hover();
await page.getByRole('menuitem', { name: 'form Table' }).click();
await page.getByText('Form').click();
await page.getByRole('menuitem', { name: 'Collapse' }).click();
await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-CardItem-roles-table-selector')).toBeVisible();
await expect(page.getByLabel('block-item-CardItem-roles-filter-form')).toBeVisible();
await expect(page.getByLabel('block-item-CardItem-roles-filter-collapse')).toBeVisible();
await expect(page.getByLabel('block-item-Markdown.Void-roles-form')).toBeVisible();
});
}); });

View File

@ -0,0 +1,48 @@
import { NocoPage, Page, expect, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
test.describe('tabs schema settings', () => {
let commonPage: NocoPage;
let commonPageUrl: string;
test.beforeAll(async ({ mockManualDestroyPage }) => {
commonPage = await mockManualDestroyPage(oneEmptyTableBlockWithActions).waitForInit();
commonPageUrl = await commonPage.getUrl();
});
test.afterAll(async () => {
await commonPage.destroy();
});
async function showSettings(page: Page) {
await page.getByTestId('drawer-Action.Container-general-Add record').getByText('Add new').hover();
await page.getByLabel('designer-schema-settings-Tabs.TabPane-Tabs.Designer-general').hover();
}
test('edit', async ({ page }) => {
await page.goto(commonPageUrl);
await page.getByRole('button', { name: 'Add new' }).click();
await showSettings(page);
await page.getByRole('menuitem', { name: 'Edit', exact: true }).click();
await page.mouse.move(300, 0);
await page.getByLabel('block-item-Input-general-Tab name').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Tab name').getByRole('textbox').fill('Add new with new name');
await page.getByRole('button', { name: 'Select icon' }).click();
await page.getByLabel('account-book').locator('svg').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('Add new with new name')).toBeVisible();
await expect(page.getByLabel('account-book').locator('svg')).toBeVisible();
});
test('delete', async ({ page }) => {
await page.goto(commonPageUrl);
await page.getByRole('button', { name: 'Add new' }).click();
await showSettings(page);
await page.getByRole('menuitem', { name: 'Delete', exact: true }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByTestId('drawer-Action.Container-general-Add record').getByText('Add new')).toBeHidden();
});
});

View File

@ -0,0 +1,30 @@
import { expect, oneFormBlockBasedOnUsers, test } from '@nocobase/test/client';
test('fields', async ({ page, mockPage }) => {
await mockPage(oneFormBlockBasedOnUsers).goto();
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').click();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Username' }).click();
await page.getByRole('menuitem', { name: 'Email' }).click();
const sourceElement = page.getByLabel('block-item-CollectionField-users-form-users.nickname');
await sourceElement.hover();
const source = sourceElement.getByLabel('designer-drag');
await source.hover();
const targetElement = page.getByLabel('block-item-CollectionField-users-form-users.username');
await source.dragTo(targetElement);
const targetElement2 = page.getByLabel('block-item-CollectionField-users-form-users.email');
await source.hover();
await source.dragTo(targetElement2);
await sourceElement.hover();
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);
//拖拽调整排序符合预期
expect(nickname.y).toBe(max);
});
test('actions', async () => {});

View File

@ -0,0 +1,225 @@
import {
expect,
oneFormWithMultiLevelAssociationFields,
oneSubformWithMultiLevelAssociationFields,
oneTableSubformWithMultiLevelAssociationFields,
oneTableSubtableWithMultiLevelAssociationFields,
test,
} from '@nocobase/test/client';
import { T2200, T2614, T2615 } from './templatesOfBug';
test.describe('display association fields', () => {
test('form: should display correctly', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneFormWithMultiLevelAssociationFields).waitForInit();
await mockRecord('general');
await nocoPage.goto();
// 为关系字段选中一个值
await page
.getByLabel('block-item-CollectionField-general-form-general.m2oField0-m2oField0')
.getByLabel('Search')
.click();
await page.getByRole('option', { name: '1', exact: true }).click();
// 相应的字段应显示出来对应的值
await expect(page.getByLabel('block-item-CollectionField-general-form-general.m2oField0.id')).toHaveText('ID:1');
await expect(page.getByLabel('block-item-CollectionField-general-form-general.m2oField0.m2oField1')).toHaveText(
'm2oField1:1',
);
});
test('subform: should display correctly', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneSubformWithMultiLevelAssociationFields).waitForInit();
await mockRecord('general');
await nocoPage.goto();
// 为子表单中的关系字段选中一个值
await page
.getByLabel('block-item-CollectionField-m2oField1-form-m2oField1.m2oField1-m2oField1')
.getByLabel('Search')
.click();
await page.getByRole('option', { name: '1', exact: true }).click();
// 相应的字段应显示出来对应的值
await expect(page.getByLabel('block-item-CollectionField-m2oField1-form-m2oField1.m2oField1.id')).toHaveText(
'ID:1',
);
await expect(page.getByLabel('block-item-CollectionField-m2oField1-form-m2oField1.m2oField1.m2oField2')).toHaveText(
'm2oField2:1',
);
});
test('subform: load association fields', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableSubformWithMultiLevelAssociationFields).waitForInit();
const record = await mockRecord('general');
await nocoPage.goto();
// 查看详情
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-general-form-general.id-ID')).toHaveText(
`ID:${record.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField1-form-m2oField1.id-ID')).toHaveText(
`ID:${record.m2oField0.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField2-form-m2oField2.id-ID')).toHaveText(
`ID:${record.m2oField0.m2oField1.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField3-form-m2oField3.id-ID')).toHaveText(
`ID:${record.m2oField0.m2oField1.m2oField2.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField3-form-m2oField3.m2oField3-m2oField3')).toHaveText(
`m2oField3:${record.m2oField0.m2oField1.m2oField2.m2oField3.id}`,
);
await page.getByLabel('drawer-Action.Container-general-View record-mask').click();
// 编辑详情
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-general-form-general.id-ID')).toHaveText(
`ID:${record.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField1-form-m2oField1.id-ID')).toHaveText(
`ID:${record.m2oField0.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField2-form-m2oField2.id-ID')).toHaveText(
`ID:${record.m2oField0.m2oField1.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField3-form-m2oField3.id-ID')).toHaveText(
`ID:${record.m2oField0.m2oField1.m2oField2.id}`,
);
await expect(page.getByLabel('block-item-CollectionField-m2oField3-form-m2oField3.m2oField3-m2oField3')).toHaveText(
`m2oField3:${record.m2oField0.m2oField1.m2oField2.m2oField3.id}`,
);
});
test('subtable: load association fields', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableSubtableWithMultiLevelAssociationFields).waitForInit();
const record = await mockRecord('general');
await nocoPage.goto();
// 查看详情
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-general-form-general.id-ID')).toHaveText(
`ID:${record.id}`,
);
for (let index = 0; index < record.m2mField0.length; index++) {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.m2mField0-m2mField0')
.getByLabel('block-item-CollectionField-m2mField1-form-m2mField1.m2mField1-m2mField1')
.nth(index),
).toHaveText(new RegExp(record.m2mField0[index].m2mField1.map((item) => item.id).join(',')));
}
await page.getByLabel('drawer-Action.Container-general-View record-mask').click();
// 编辑详情
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-general-form-general.id-ID')).toHaveText(
`ID:${record.id}`,
);
for (let index = 0; index < record.m2mField0.length; index++) {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.m2mField0-m2mField0')
.getByLabel('block-item-CollectionField-m2mField1-form-m2mField1.m2mField1-m2mField1')
.nth(index),
).toHaveText(new RegExp(record.m2mField0[index].m2mField1.map((item) => item.id).join('')));
}
});
// https://nocobase.height.app/T-2615
test('BUG: should load association data', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2615).waitForInit();
await mockRecord('T2615');
await nocoPage.goto();
// 1. 新增表单中应该显示关系字段的数据
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o').getByLabel('Search').click();
await page.getByRole('option', { name: '1' }).click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o.m2oOfTarget1')).toHaveText(
`m2oOfTarget1:1`,
);
// 关闭弹窗
await page.getByLabel('drawer-Action.Container-T2615-Add record-mask').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 2. 编辑表单中应该显示关系字段的数据
await page.getByLabel('action-Action.Link-Edit record-update-T2615-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o')).toHaveText(`m2o:1`);
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o.m2oOfTarget1')).toHaveText(
`m2oOfTarget1:1`,
);
await page.getByLabel('drawer-Action.Container-T2615-Edit record-mask').click();
// 3. 详情中应该显示关系字段的数据
await page.getByLabel('action-Action.Link-View record-view-T2615-table-0').click();
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o-m2o')).toHaveText(`m2o:1`);
await expect(page.getByLabel('block-item-CollectionField-T2615-form-T2615.m2o.m2oOfTarget1')).toHaveText(
`m2oOfTarget1:1`,
);
});
// https://nocobase.height.app/T-2614
test('BUG: should load association data in subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2614).waitForInit();
await mockRecord('T2614');
await nocoPage.goto();
// 查看详情
await page.getByLabel('action-Action.Link-View record-view-T2614-table-0').click();
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1-m2oOfTarget1'),
).toHaveText(`m2oOfTarget1:1`);
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1.id'),
).toHaveText(`ID:1`);
// 关闭弹窗
await page.getByLabel('drawer-Action.Container-T2614-View record-mask').click();
// 编辑详情
await page.getByLabel('action-Action.Link-Edit record-update-T2614-table-0').click();
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1-m2oOfTarget1'),
).toHaveText(`m2oOfTarget1:1`);
await expect(
page.getByLabel('block-item-CollectionField-T2614Target1-form-T2614Target1.m2oOfTarget1.id'),
).toHaveText(`ID:1`);
});
});
test.describe('association fields', () => {
// fix https://nocobase.height.app/T-2200
test('BUG: should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
await mockPage(T2200).goto();
await page.getByLabel('action-Action.Link-Edit-update-users-table-0').click();
await expect(page.getByLabel('Admin')).toBeVisible();
await expect(page.getByLabel('Member')).toBeVisible();
await expect(page.getByLabel('Root')).toBeVisible();
await page.getByTestId('select-object-multiple').click();
await page.getByRole('option', { name: 'Member' }).click();
// 再次点击,关闭下拉框。
await page.getByTestId('select-object-multiple').click();
await expect(page.getByLabel('Admin')).toBeVisible();
await expect(page.getByLabel('Member')).toBeHidden();
await expect(page.getByLabel('Root')).toBeVisible();
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.mouse.move(200, 0);
await page.waitForTimeout(200);
await expect(page.getByLabel('Admin')).toBeVisible();
await expect(page.getByLabel('Member')).toBeHidden();
await expect(page.getByLabel('Root')).toBeVisible();
});
});

View File

@ -1,9 +1,8 @@
import { expect, test } from '@nocobase/test/client'; import { expect, formBlockDefaultValueTemplate, test } from '@nocobase/test/client';
import { testingLazyLoadingOfAssociationFieldsForTheCurrentRecord } from './templatesOfPage';
test.describe('default value: lazy load value of variables', () => { test.describe('variables with default value', () => {
test('current form', async ({ page, mockPage, mockRecord }) => { test('current form', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(testingLazyLoadingOfAssociationFieldsForTheCurrentRecord).waitForInit(); const nocoPage = await mockPage(formBlockDefaultValueTemplate).waitForInit();
await mockRecord('general'); await mockRecord('general');
await nocoPage.goto(); await nocoPage.goto();

View File

@ -1,12 +1,23 @@
import { expect, oneEmptyForm, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyForm, test } from '@nocobase/test/client';
test.describe('Form', () => { test.describe('where creation form block can be added', () => {
test('add fields', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Form');
await expect(page.getByLabel('block-item-CardItem-users-form')).toBeVisible();
});
});
test.describe('configure fields', () => {
test('display collection fields & display association fields & add text', async ({ page, mockPage }) => {
await mockPage(oneEmptyForm).goto(); await mockPage(oneEmptyForm).goto();
// collection fields
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-general').hover(); await page.getByLabel('schema-initializer-Grid-FormItemInitializers-general').hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).toBeChecked();
// add association fields // add association fields
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover(); await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
@ -21,8 +32,8 @@ test.describe('Form', () => {
// delete fields // delete fields
await page.getByLabel('schema-initializer-Grid-FormItemInitializers-general').hover(); await page.getByLabel('schema-initializer-Grid-FormItemInitializers-general').hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).not.toBeChecked();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover(); await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click(); await page.getByRole('menuitem', { name: 'Nickname' }).click();
@ -42,9 +53,11 @@ test.describe('Form', () => {
await expect(page.getByLabel('block-item-Markdown.Void-general-form')).toBeVisible(); await expect(page.getByLabel('block-item-Markdown.Void-general-form')).toBeVisible();
}); });
test.pgOnly('add inherit fields', async ({ page, mockPage }) => {}); test.pgOnly('display inherit fields', async ({ page, mockPage }) => {});
});
test('add action buttons', async ({ page, mockPage }) => { test.describe('configure actions', () => {
test('submit', async ({ page, mockPage }) => {
await mockPage(oneEmptyForm).goto(); await mockPage(oneEmptyForm).goto();
await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-general').hover(); await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-general').hover();
@ -65,7 +78,7 @@ test.describe('Form', () => {
await expect(page.getByRole('button', { name: 'Submit' })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'Submit' })).not.toBeVisible();
}); });
test('add custom action buttons', async ({ page, mockPage }) => { test('customize: save record', async ({ page, mockPage }) => {
await mockPage(oneEmptyForm).goto(); await mockPage(oneEmptyForm).goto();
await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-general').hover(); await page.getByLabel('schema-initializer-ActionBar-FormActionInitializers-general').hover();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
import { expect, oneEmptyTableBlockWithActions, test } from '@nocobase/test/client';
test.describe('where edit form block can be added', () => {
test('popup', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByLabel('action-Action.Link-Edit-update-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover();
await page.getByText('Form').click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-general-form')).toBeVisible();
});
});
test.describe('configure actions', () => {});
test.describe('configure fields', () => {});

View File

@ -0,0 +1,175 @@
import {
Page,
expect,
expectSettingsMenu,
oneEmptyFormWithActions,
oneTableBlockWithActionsAndFormBlocks,
test,
} from '@nocobase/test/client';
const clickOption = async (page: Page, optionName: string) => {
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await page.getByRole('menuitem', { name: optionName }).click();
};
test.describe('edit form block schema settings', () => {
test('Edit block title', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
// 打开编辑弹窗
await clickOption(page, 'Edit block title');
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('Block title 123');
await page.getByRole('button', { name: 'OK', exact: true }).click();
const runExpect = async () => {
// 设置成功后,显示在区块顶部
await expect(
page.getByLabel('block-item-CardItem-general-form').getByText('Block title 123', { exact: true }),
).toBeVisible();
// 再次打开编辑弹窗时,显示的是上次设置的值
await clickOption(page, 'Edit block title');
await expect(page.getByRole('textbox')).toHaveValue('Block title 123');
};
await runExpect();
// 刷新页面后,显示的应该依然是上次设置的值
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await runExpect();
});
test('Linkage rules', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
// 打开编辑弹窗
await clickOption(page, 'Linkage rules');
await page.getByRole('button', { name: 'Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-property-field').click();
await page.getByText('singleLineText', { exact: true }).click();
await page.getByTestId('select-linkage-action-field').click();
await page.getByRole('option', { name: 'Visible' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
const runExpect = async () => {
await expect(page.getByTestId('select-linkage-property-field').getByText('singleLineText')).toBeVisible();
await expect(page.getByTestId('select-linkage-action-field').getByText('Visible')).toBeVisible();
};
// 再次打开,设置的值应该存在
await clickOption(page, 'Linkage rules');
await runExpect();
// 刷新页面后,设置的值应该依然存在
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await clickOption(page, 'Linkage rules');
await runExpect();
});
test('Convert reference to duplicate & Save as block template', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
// 打开编辑弹窗
await clickOption(page, 'Save as block template');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 设置成功后,不再显示 Save as block template 选项,而是显示 Convert reference to duplicate 选项
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).toBeVisible();
// 刷新页面
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).toBeVisible();
// Convert reference to duplicate
await clickOption(page, 'Convert reference to duplicate');
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).not.toBeVisible();
// 刷新页面
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
await expect(page.getByRole('menuitem', { name: 'Save as block template' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Convert reference to duplicate' })).not.toBeVisible();
});
test('Delete', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await page.getByText('Edit', { exact: true }).click();
await page.getByLabel('block-item-CardItem-general-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.Designer-general').hover();
// 打开编辑弹窗
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 设置成功后,显示在区块顶部
await expect(page.getByLabel('block-item-CardItem-general-form')).not.toBeVisible();
// 刷新页面后,区块依然是被删除状态
await page.reload();
await page.getByText('Edit', { exact: true }).click();
await expect(page.getByLabel('block-item-CardItem-general-form')).not.toBeVisible();
});
});
test.describe('actions schema settings', () => {
test('submit', async ({ page, mockPage }) => {
await mockPage(oneEmptyFormWithActions).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Submit' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-users' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Bind workflows', 'Delete'],
});
});
test('customize: save record', async ({ page, mockPage }) => {
await mockPage(oneEmptyFormWithActions).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Save record' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-users' }).hover();
},
supportedOptions: [
'Edit button',
'Linkage rules',
'Assign field values',
'Skip required validation',
'After successful submission',
'Bind workflows',
'Delete',
],
});
});
});

View File

@ -1,23 +1,33 @@
import { expect, oneEmptyFilterFormBlock, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyFilterFormBlock, test } from '@nocobase/test/client';
test.describe('Filter Form Block', () => { test.describe('where filter form block can be added', () => {
test('add fields', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Filter form');
await expect(page.getByLabel('block-item-CardItem-users-filter-form')).toBeVisible();
});
});
test.describe('configure fields', () => {
test('display collection fields & display association fields & add text', async ({ page, mockPage }) => {
await mockPage(oneEmptyFilterFormBlock).goto(); await mockPage(oneEmptyFilterFormBlock).goto();
const formItemInitializer = page.getByLabel('schema-initializer-Grid-FilterFormItemInitializers-general'); const formItemInitializer = page.getByLabel('schema-initializer-Grid-FilterFormItemInitializers-general');
// add fields // add fields
await formItemInitializer.hover(); await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-filter-form-general.id-ID')).toBeVisible(); await expect(page.getByLabel('block-item-CollectionField-general-filter-form-general.id-ID')).toBeVisible();
// delete fields // delete fields
await formItemInitializer.hover(); await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-filter-form-general.id-ID')).not.toBeVisible(); await expect(page.getByLabel('block-item-CollectionField-general-filter-form-general.id-ID')).not.toBeVisible();
@ -55,9 +65,11 @@ test.describe('Filter Form Block', () => {
await expect(page.getByLabel('block-item-Markdown.Void-general-filter-form')).toBeVisible(); await expect(page.getByLabel('block-item-Markdown.Void-general-filter-form')).toBeVisible();
}); });
test.pgOnly('add inherit fields', async ({ page, mockPage }) => {}); test.pgOnly('display inherit fields', async ({ page, mockPage }) => {});
});
test('add action buttons', async ({ page, mockPage }) => { test.describe('configure actions', () => {
test('filter & reset', async ({ page, mockPage }) => {
await mockPage(oneEmptyFilterFormBlock).goto(); await mockPage(oneEmptyFilterFormBlock).goto();
await page.getByLabel('schema-initializer-ActionBar-FilterFormActionInitializers-general').hover(); await page.getByLabel('schema-initializer-ActionBar-FilterFormActionInitializers-general').hover();

View File

@ -0,0 +1,108 @@
import {
expect,
expectSettingsMenu,
oneEmptyFilterFormBlock,
oneFormAndOneTableWithSameCollection,
test,
} from '@nocobase/test/client';
test.describe('filter block schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyFilterFormBlock).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CardItem-general-filter-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.FilterDesigner-general').hover();
},
supportedOptions: [
'Edit block title',
'Save as block template',
'Linkage rules',
'Connect data blocks',
'Delete',
],
});
});
test.describe('connect data blocks', () => {
test('connecting two blocks of the same collection', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneFormAndOneTableWithSameCollection).waitForInit();
const records = await mockRecords('general', 3);
await nocoPage.goto();
// 将上面的 Form 连接到下面的 Table
await page.getByLabel('block-item-CardItem-general-filter-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-FormV2.FilterDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Connect data blocks' }).hover();
await page.getByRole('menuitem', { name: 'General' }).click();
// 输入值,点击筛选按钮
await page
.getByLabel('block-item-CollectionField-general-filter-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.click();
await page
.getByLabel('block-item-CollectionField-general-filter-form-general.singleLineText-singleLineText')
.getByRole('textbox')
.fill(records[0].singleLineText);
// 点击筛选按钮
await page.getByLabel('action-Action-Filter records-submit-general-filter-form').click();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: records[1].singleLineText }),
).toBeHidden();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: records[2].singleLineText }),
).toBeHidden();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: records[0].singleLineText }),
).toBeVisible();
// 点击重置按钮
await page.getByLabel('action-Action-Reset records-general-filter-form').click();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: records[1].singleLineText }),
).toBeVisible();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: records[2].singleLineText }),
).toBeVisible();
await expect(
page.getByLabel('block-item-CardItem-general-table').getByRole('row', { name: records[0].singleLineText }),
).toBeVisible();
});
});
});
test.describe('actions schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyFilterFormBlock).goto();
// 创建 Filter & Reset 两个按钮
await page.getByLabel('schema-initializer-ActionBar-FilterFormActionInitializers-general').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Reset' }).click();
// Filter settings -------------------------------------------------------------------
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action-Filter-submit-general-filter-form').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Delete'],
});
// Reset settings --------------------------------------------------------------------
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action-Reset-general-filter-form').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Delete'],
});
});
});

View File

@ -1,7 +1,19 @@
import { expect, oneEmptyGridCardBlock, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyGridCardBlock, test } from '@nocobase/test/client';
test.describe('Grid Card', () => { test.describe('where grid card block can be added', () => {
test('add actions', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Grid Card');
await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible();
});
test('data selector popup', async ({ page, mockPage }) => {});
});
test.describe('configure global actions', () => {
test('filter & add new & refresh', async ({ page, mockPage }) => {
await mockPage(oneEmptyGridCardBlock).goto(); await mockPage(oneEmptyGridCardBlock).goto();
await page.getByLabel('schema-initializer-ActionBar-GridCardActionInitializers-general').hover(); await page.getByLabel('schema-initializer-ActionBar-GridCardActionInitializers-general').hover();
@ -33,66 +45,10 @@ test.describe('Grid Card', () => {
await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible();
}); });
});
test('add item fields', async ({ page, mockPage, mockRecord }) => { test.describe('configure item actions', () => {
const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit(); test('view & edit & delete', async ({ page, mockPage, mockRecord }) => {
await mockRecord('general');
await nocoPage.goto();
const formItemInitializer = page
.getByLabel('schema-initializer-Grid-ReadPrettyFormItemInitializers-general')
.first();
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked();
// add association fields
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-grid-card-general.id-ID').first()).toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-grid-card-general.manyToOne.nickname').first(),
).toBeVisible();
// delete fields
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked();
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(
page.getByLabel('block-item-CollectionField-general-grid-card-general.id-ID').first(),
).not.toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-grid-card-general.manyToOne.nickname').first(),
).not.toBeVisible();
// add text
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).hover();
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-Markdown.Void-general-grid-card').first()).toBeVisible();
});
test.pgOnly('add inherit fields', async ({ page, mockPage }) => {});
test('add item actions', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit(); const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit();
await mockRecord('general'); await mockRecord('general');
await nocoPage.goto(); await nocoPage.goto();
@ -127,7 +83,7 @@ test.describe('Grid Card', () => {
await expect(page.getByLabel('action-Action.Link-Delete-destroy-general-grid-card').first()).not.toBeVisible(); await expect(page.getByLabel('action-Action.Link-Delete-destroy-general-grid-card').first()).not.toBeVisible();
}); });
test('add item custom action buttons', async ({ page, mockPage, mockRecord }) => { test('customize: popup & update record', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit(); const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit();
await mockRecord('general'); await mockRecord('general');
await nocoPage.goto(); await nocoPage.goto();
@ -144,3 +100,62 @@ test.describe('Grid Card', () => {
).toBeVisible(); ).toBeVisible();
}); });
}); });
test.describe('configure fields', () => {
test('display collection fields & display association fields & add text', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit();
await mockRecord('general');
await nocoPage.goto();
const formItemInitializer = page
.getByLabel('schema-initializer-Grid-ReadPrettyFormItemInitializers-general')
.first();
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).toBeChecked();
// add association fields
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-grid-card-general.id-ID').first()).toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-grid-card-general.manyToOne.nickname').first(),
).toBeVisible();
// delete fields
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).not.toBeChecked();
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(
page.getByLabel('block-item-CollectionField-general-grid-card-general.id-ID').first(),
).not.toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-grid-card-general.manyToOne.nickname').first(),
).not.toBeVisible();
// add text
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID', exact: true }).hover();
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-Markdown.Void-general-grid-card').first()).toBeVisible();
});
test.pgOnly('display inherit fields', async () => {});
});

View File

@ -0,0 +1,51 @@
import { expect, expectSettingsMenu, oneEmptyGridCardBlock, test } from '@nocobase/test/client';
test.describe('grid card block schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyGridCardBlock).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-BlockItem-general-grid-card').hover();
await page.getByLabel('designer-schema-settings-BlockItem-GridCard.Designer-general').hover();
},
supportedOptions: [
'Set the count of columns displayed in a row',
'Set the data scope',
'Set default sorting rules',
'Records per page',
'Save as template',
'Delete',
],
});
});
test('set the count of columns displayed in a row', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneEmptyGridCardBlock).waitForInit();
await mockRecords('general', 10);
await nocoPage.goto();
let boxSize = await page.getByLabel('grid-card-item').first().boundingBox();
// 默认是 3 列
// 之所以这样断言,是因为不同平台的浏览器渲染的 width 可能会有一点点差异,所以这里只能用一个范围来判断
expect(boxSize.width).toBeGreaterThan(400);
expect(boxSize.width).toBeLessThan(410);
// 修改成 2 列(在桌面端)
await page.getByLabel('block-item-BlockItem-general-grid-card').hover();
await page.getByLabel('designer-schema-settings-BlockItem-GridCard.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Set the count of columns displayed in a row' }).click();
await page.getByLabel('block-item-Slider-general-Desktop device').getByText('2', { exact: true }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 需要刷新页面才会生效
await page.reload();
boxSize = await page.getByLabel('grid-card-item').first().boundingBox();
expect(boxSize.width).toBeGreaterThan(600);
expect(boxSize.width).toBeLessThan(620);
});
});
test.describe('actions schema settings', () => {});

View File

@ -1,7 +1,17 @@
import { expect, oneEmptyListBlock, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyListBlock, test } from '@nocobase/test/client';
test.describe('List', () => { test.describe('where list block can be added', () => {
test('add list actions', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'List');
await expect(page.getByLabel('block-item-CardItem-users-list')).toBeVisible();
});
});
test.describe('configure global actions', () => {
test('filter & add new & refresh', async ({ page, mockPage }) => {
await mockPage(oneEmptyListBlock).goto(); await mockPage(oneEmptyListBlock).goto();
await page.getByLabel('schema-initializer-ActionBar-ListActionInitializers-general').hover(); await page.getByLabel('schema-initializer-ActionBar-ListActionInitializers-general').hover();
@ -33,61 +43,10 @@ test.describe('List', () => {
await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible();
}); });
});
test('add fields', async ({ page, mockPage, mockRecord }) => { test.describe('configure item actions', () => {
const nocoPage = await mockPage(oneEmptyListBlock).waitForInit(); test('view & edit & delete', async ({ page, mockPage, mockRecord }) => {
await mockRecord('general');
await nocoPage.goto();
const formItemInitializer = page
.getByLabel('schema-initializer-Grid-ReadPrettyFormItemInitializers-general')
.first();
// add fields
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID' }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked();
// add association fields
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
// TODO: 二级菜单点击后,不应该被关闭;只有在鼠标移出下拉列表的时候,才应该被关闭
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-list-general.id-ID').first()).toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-list-general.manyToOne.nickname').first(),
).toBeVisible();
// delete fields
await formItemInitializer.hover();
await page.getByRole('tooltip').getByText('Display association fields').hover();
await page.mouse.wheel(0, -300);
await page.getByRole('menuitem', { name: 'ID' }).click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-list-general.id-ID').first()).not.toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-list-general.manyToOne.nickname').first(),
).not.toBeVisible();
// add text
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-Markdown.Void-general-list').first()).toBeVisible();
});
test('add item actions', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyListBlock).waitForInit(); const nocoPage = await mockPage(oneEmptyListBlock).waitForInit();
await mockRecord('general'); await mockRecord('general');
await nocoPage.goto(); await nocoPage.goto();
@ -122,7 +81,7 @@ test.describe('List', () => {
await expect(page.getByLabel('action-Action.Link-Delete-destroy-general-list').first()).not.toBeVisible(); await expect(page.getByLabel('action-Action.Link-Delete-destroy-general-list').first()).not.toBeVisible();
}); });
test('add custom action buttons', async ({ page, mockPage, mockRecord }) => { test('customize: popup & update record', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyListBlock).waitForInit(); const nocoPage = await mockPage(oneEmptyListBlock).waitForInit();
await mockRecord('general'); await mockRecord('general');
await nocoPage.goto(); await nocoPage.goto();
@ -139,3 +98,58 @@ test.describe('List', () => {
).toBeVisible(); ).toBeVisible();
}); });
}); });
test.describe('configure fields', () => {
test('display collection fields & display association fields & add text', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyListBlock).waitForInit();
await mockRecord('general');
await nocoPage.goto();
const formItemInitializer = page
.getByLabel('schema-initializer-Grid-ReadPrettyFormItemInitializers-general')
.first();
// add fields
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).toBeChecked();
// add association fields
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
// TODO: 二级菜单点击后,不应该被关闭;只有在鼠标移出下拉列表的时候,才应该被关闭
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-list-general.id-ID').first()).toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-list-general.manyToOne.nickname').first(),
).toBeVisible();
// delete fields
await formItemInitializer.hover();
await page.getByRole('tooltip').getByText('Display association fields').hover();
await page.mouse.wheel(0, -300);
await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).not.toBeChecked();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.getByRole('menuitem', { name: 'Many to one' }).nth(1).hover();
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-general-list-general.id-ID').first()).not.toBeVisible();
await expect(
page.getByLabel('block-item-CollectionField-general-list-general.manyToOne.nickname').first(),
).not.toBeVisible();
// add text
await formItemInitializer.hover();
await page.getByRole('menuitem', { name: 'Add text' }).click();
await expect(page.getByLabel('block-item-Markdown.Void-general-list').first()).toBeVisible();
});
});

View File

@ -0,0 +1,25 @@
import { expectSettingsMenu, oneEmptyListBlock, test } from '@nocobase/test/client';
test.describe('list block schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyListBlock).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CardItem-general-list').hover();
await page.getByLabel('designer-schema-settings-CardItem-List.Designer-general').hover();
},
supportedOptions: [
'Edit block title',
'Set the data scope',
'Set default sorting rules',
'Records per page',
'Save as template',
'Delete',
],
});
});
});
test.describe('actions schema settings', () => {});

View File

@ -0,0 +1,11 @@
import { createBlockInPage, expect, test } from '@nocobase/test/client';
test.describe('where markdown block can be added', () => {
test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Markdown');
await expect(page.getByLabel('block-item-Markdown.Void-markdown')).toBeVisible();
});
});

View File

@ -0,0 +1,31 @@
import { expect, expectSettingsMenu, oneEmptyMarkdown, test } from '@nocobase/test/client';
test.describe('markdown block schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyMarkdown).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-Markdown.Void-markdown').hover();
await page.getByLabel('designer-schema-settings-Markdown.Void-Markdown.Void.Designer').hover();
},
supportedOptions: ['Edit markdown', 'Delete'],
});
});
test('edit markdown', async ({ page, mockPage }) => {
await mockPage(oneEmptyMarkdown).goto();
await page.getByLabel('block-item-Markdown.Void-markdown').hover();
await page.getByLabel('designer-schema-settings-Markdown.Void-Markdown.Void.Designer').hover();
await page.getByRole('menuitem', { name: 'Edit markdown' }).click();
// 输入新的值,并保存
await page.getByLabel('block-item-Markdown.Void-markdown').getByRole('textbox').click();
await page.getByLabel('block-item-Markdown.Void-markdown').getByRole('textbox').fill('hello markdown');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByLabel('block-item-Markdown.Void-markdown').getByText('hello markdown')).toBeVisible();
});
});

View File

@ -0,0 +1,22 @@
import { expect, test } from '@nocobase/test/client';
test('single page', async ({ page, mockPage }) => {
const pageTitle1 = 'page1';
const pageTitle2 = 'page2';
await mockPage({
name: pageTitle1,
}).goto();
await mockPage({ name: pageTitle2 }).goto();
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.getByRole('menuitem', { name: 'Move to' }).click();
await page.getByRole('dialog').click();
await page.getByLabel('Search').click();
await page.locator('.ant-select-dropdown').getByText(pageTitle2).click();
await page.getByRole('button', { name: 'OK', exact: true }).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);
});

View File

@ -1,6 +1,6 @@
import { expect, groupPageEmpty, test } from '@nocobase/test/client'; import { expect, groupPageEmpty, test } from '@nocobase/test/client';
test.describe('Menu', () => { test.describe('add menu item', () => {
test('header', async ({ page, deletePage }) => { test('header', async ({ page, deletePage }) => {
await page.goto('/'); await page.goto('/');

View File

@ -0,0 +1,225 @@
import { Page, PageConfig, expect, expectSettingsMenu, test } from '@nocobase/test/client';
test.describe('group page menus schema settings', () => {
test('edit', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'group page' }).goto();
await showSettings(page, 'group page');
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.mouse.move(300, 0);
// 设置一个新名称
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('new group page');
// 设置一个图标
await page.getByRole('button', { name: 'Select icon' }).click();
await page.getByRole('tooltip').getByLabel('account-book').locator('svg').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.reload();
await expect(page.getByLabel('new group page')).toBeVisible();
await expect(page.getByLabel('new group page').getByLabel('account-book').locator('svg')).toBeVisible();
});
test('move to', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'anchor page' }).waitForInit();
await mockPage({ type: 'group', name: 'a other group page' }).waitForInit();
await mockPage({ type: 'group', name: 'group page' }).goto();
// 默认情况下的排列顺序anchor page, group page
await expect(page.getByText('anchor pagea other group pagegroup page')).toBeVisible();
// 移动到 anchor page 之前 --------------------------------------------
await showSettings(page, 'group page');
await page.getByRole('menuitem', { name: 'Move to' }).click();
await page.mouse.move(300, 0);
await page.getByRole('dialog').getByLabel('Search').click();
await page.locator('.ant-select-dropdown').getByText('anchor page').click();
await page.getByLabel('Before').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('group pageanchor pagea other group page')).toBeVisible();
// 移动到 anchor page 之后
await showSettings(page, 'group page');
await page.getByRole('menuitem', { name: 'Move to' }).click();
await page.mouse.move(300, 0);
await page.getByRole('dialog').getByLabel('Search').click();
await page.locator('.ant-select-dropdown').getByText('anchor page').click();
await page.getByLabel('After').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('anchor pagegroup pagea other group page')).toBeVisible();
// 移动到 anchor page 内部 --------------------------------------------
await showSettings(page, 'group page');
await page.getByRole('menuitem', { name: 'Move to' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('Search').click();
await page.locator('.ant-select-dropdown').getByText('anchor page').click();
await page.getByLabel('Inner').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 当前页面菜单会消失
await expect(page.getByLabel('group page', { exact: true })).not.toBeVisible();
// 跳转到 anchor page 页面,会有一个名为 group page 的子页面菜单
await page.getByLabel('anchor page').click();
await expect(page.locator('.ant-layout-sider').getByLabel('group page')).toBeVisible();
// 移动到子页面菜单之前 --------------------------------------------
await showSettings(page, 'a other group page');
await page.getByRole('menuitem', { name: 'Move to' }).click();
await page.mouse.move(300, 0);
await page.getByRole('dialog').getByLabel('Search').click();
await page.getByLabel('caret-down').locator('svg').click();
await page.locator('.ant-select-dropdown').getByText('group page', { exact: true }).click();
await page.getByLabel('Before').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.locator('.ant-layout-sider').getByText('a other group pagegroup page')).toBeVisible();
});
test('insert before', async ({ page, mockPage, deletePage }) => {
await mockPage({ name: 'single page' }).goto();
// 在 single page 之前插入一个 group page
await showSettings(page, 'single page');
await page.getByRole('menuitem', { name: 'Insert before' }).hover();
await page.getByRole('menuitem', { name: 'Group', exact: true }).click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').fill('group page');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.locator('.ant-layout-header').getByText('group pagesingle page')).toBeVisible();
await deletePage('group page');
});
test('insert after', async ({ page, mockPage, deletePage }) => {
await mockPage({ name: 'single page' }).goto();
// 在 single page 之后插入一个 group page
await showSettings(page, 'single page');
await page.getByRole('menuitem', { name: 'Insert after' }).hover();
await page.getByRole('menuitem', { name: 'Group', exact: true }).click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').fill('group page');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.locator('.ant-layout-header').getByText('single pagegroup page')).toBeVisible();
await deletePage('group page');
});
test('insert inner', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'group page' }).goto();
// 在 group page 内部插入一个 single page
await showSettings(page, 'group page');
await page.getByRole('menuitem', { name: 'Insert inner' }).hover();
await page.getByRole('menuitem', { name: 'Page', exact: true }).click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').fill('single page');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.locator('.ant-layout-sider').getByText('single page')).toBeVisible();
});
test('delete', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'group page' }).goto();
await expect(page.getByLabel('group page')).toBeVisible();
await showSettings(page, 'group page');
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 现在需要刷新一下页面,被删除的页面内容才会消失
await page.reload();
await expect(page.getByLabel('group page')).not.toBeVisible();
});
});
test.describe('group page side menus schema settings', () => {
test('group page', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'group page' }).goto();
await createSubPage({ page, type: 'group', name: 'group page in side' });
await expectSettingsMenu({
page,
showMenu: () => showSettingsInSide(page, 'group page in side'),
supportedOptions: ['Edit', 'Move to', 'Insert before', 'Insert after', 'Insert inner', 'Delete'],
});
});
test('link page', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'group page' }).goto();
await createSubPage({ page, type: 'link', name: 'link page in side' });
await expectSettingsMenu({
page,
showMenu: () => showSettingsInSide(page, 'link page in side'),
supportedOptions: ['Edit', 'Move to', 'Insert before', 'Insert after', 'Delete'],
});
});
test('single page', async ({ page, mockPage }) => {
await mockPage({ type: 'group', name: 'group page' }).goto();
await createSubPage({ page, type: 'page', name: 'single page in side' });
await expectSettingsMenu({
page,
showMenu: () => showSettingsInSide(page, 'single page in side'),
supportedOptions: ['Edit', 'Move to', 'Insert before', 'Insert after', 'Delete'],
});
});
});
test.describe('link page menu schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage({ type: 'link', name: 'link page' }).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.locator('.ant-layout-header').getByText('link page', { exact: true }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-' }).hover();
},
supportedOptions: ['Edit', 'Move to', 'Insert before', 'Insert after', 'Delete'],
});
});
});
test.describe('single page menu schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage({ name: 'single page' }).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.locator('.ant-layout-header').getByText('single page', { exact: true }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-' }).hover();
},
supportedOptions: ['Edit', 'Move to', 'Insert before', 'Insert after', 'Delete'],
});
});
});
async function showSettingsInSide(page: Page, pageName: string) {
await page.locator('.ant-layout-sider').getByText(pageName, { exact: true }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-' }).hover();
}
async function showSettings(page: Page, pageName: string) {
await page.locator('.ant-layout-header').getByText(pageName, { exact: true }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-' }).hover();
}
async function createSubPage({ page, name, type }: { page: Page; name: string; type: PageConfig['type'] }) {
const typeToOptionName = {
group: 'Group',
page: 'Page',
link: 'Link',
};
await page.getByTestId('schema-initializer-Menu-side').hover();
await page.getByRole('menuitem', { name: typeToOptionName[type], exact: true }).click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-Menu item title').getByRole('textbox').fill(name);
await page.getByRole('button', { name: 'OK', exact: true }).click();
}

View File

@ -0,0 +1,21 @@
import { expect, test, twoTabsPage } from '@nocobase/test/client';
test('tabs', async ({ page, mockPage }) => {
await mockPage(twoTabsPage).goto();
const sourceElement = page.locator('span:has-text("tab 2")');
await sourceElement.hover();
const source = page.getByRole('button', { name: 'drag' });
await source.hover();
const targetElement = 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
expect(tab2.x).toBeLessThan(tab1.x);
});

View File

@ -0,0 +1,13 @@
import { expect, tabPageEmpty, test } from '@nocobase/test/client';
test('add tab', async ({ page, mockPage }) => {
await mockPage(tabPageEmpty).goto();
// add tab page
await page.getByLabel('schema-initializer-Page-tabs').click();
await page.getByRole('textbox').click();
await page.getByRole('textbox').fill('tab1');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('tab1', { exact: true })).toBeVisible();
});

View File

@ -0,0 +1,103 @@
import { Page, expect, test } from '@nocobase/test/client';
test.describe('page schema settings', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByLabel('designer-schema-settings-Page').hover();
};
test('enable page header & display page title & edit page title & enable page tabs', async ({ page, mockPage }) => {
await mockPage({ name: 'page' }).goto();
// 选项Enable page header ---------------------------------------------------------------------------------
await expect(page.locator('.ant-page-header')).toBeVisible();
await showMenu(page);
// 默认开启
await expect(page.getByRole('menuitem', { name: 'Enable page header' }).getByRole('switch')).toBeChecked();
await page.getByRole('menuitem', { name: 'Enable page header' }).click();
await expect(page.getByRole('menuitem', { name: 'Enable page header' }).getByRole('switch')).not.toBeChecked();
// 标题应该被隐藏
await expect(page.locator('.ant-page-header')).not.toBeVisible();
// 其它开关也应该被隐藏
await expect(page.getByRole('menuitem', { name: 'Display page title' })).not.toBeVisible();
// 选项Display page title ---------------------------------------------------------------------------------
// 再次开启,显示出 Display page title
await page.getByRole('menuitem', { name: 'Enable page header' }).click();
await expect(page.getByRole('menuitem', { name: 'Display page title' }).getByRole('switch')).toBeChecked();
await page.getByRole('menuitem', { name: 'Display page title' }).click();
await expect(page.getByRole('menuitem', { name: 'Display page title' }).getByRole('switch')).not.toBeChecked();
// 标题应该被隐藏
await expect(page.locator('.ant-page-header').getByText('page')).not.toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Edit page title' })).not.toBeVisible();
// 选项Edit page title ------------------------------------------------------------------------------------
// 再次开启,显示出 Edit page title
await page.getByRole('menuitem', { name: 'Display page title' }).click();
await page.getByRole('menuitem', { name: 'Edit page title' }).click();
await page.getByLabel('block-item-Input-Title').getByRole('textbox').fill('new page title');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('new page title')).toBeVisible();
// 选项Enable page tabs -----------------------------------------------------------------------------------
await showMenu(page);
// 默认关闭
await expect(page.getByRole('menuitem', { name: 'Enable page tabs' }).getByRole('switch')).not.toBeChecked();
await page.getByRole('menuitem', { name: 'Enable page tabs' }).click();
await expect(page.getByRole('menuitem', { name: 'Enable page tabs' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
// 标签选项应该显示
await expect(page.getByLabel('schema-initializer-Page-tabs')).toBeVisible();
// 添加一个新的 tab 页
await page.getByLabel('schema-initializer-Page-tabs').click();
await page.getByLabel('block-item-Input-Tab name').getByRole('textbox').click();
await page.getByLabel('block-item-Input-Tab name').getByRole('textbox').fill('new tab');
// 选择一个图标
await page.getByRole('button', { name: 'Select icon' }).click();
await page.getByLabel('account-book').locator('svg').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('new tab')).toBeVisible();
await expect(page.getByLabel('account-book').locator('svg')).toBeVisible();
});
});
test.describe('tabs schema settings', () => {
async function showSettings(page: Page) {
await page.getByText('Unnamed').hover();
await page.getByRole('tab').getByLabel('designer-schema-settings-Page').hover();
}
async function enablePageTabs(page) {
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Page' }).hover();
await page.getByRole('menuitem', { name: 'Enable page tabs' }).click();
await page.mouse.move(300, 0);
}
test('edit', async ({ page, mockPage }) => {
await mockPage().goto();
await enablePageTabs(page);
await showSettings(page);
await page.getByRole('menuitem', { name: 'Edit', exact: true }).click();
await page.getByLabel('block-item-Input-Tab name').getByRole('textbox').click();
await page.getByLabel('block-item-Input-Tab name').getByRole('textbox').fill('new name of page tab');
await page.getByRole('button', { name: 'Select icon' }).click();
await page.getByLabel('account-book').locator('svg').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('new name of page tab')).toBeVisible();
await expect(page.getByLabel('account-book').locator('svg')).toBeVisible();
});
test('delete', async ({ page, mockPage }) => {
await mockPage().goto();
await enablePageTabs(page);
await showSettings(page);
await page.getByRole('menuitem', { name: 'Delete', exact: true }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText('Unnamed')).toBeHidden();
});
});

View File

@ -7,13 +7,13 @@ async function waitForModalToBeHidden(page) {
const computedStyle = window.getComputedStyle(modal); const computedStyle = window.getComputedStyle(modal);
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden'; return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
} }
return true; // 如果找不到modal也算作不可见 return true; // if the modal cannot be found, consider it hidden
}); });
} }
test.describe('add plugin in front', () => { test.describe('add plugin in front', () => {
test.slow(); test.slow();
test('add plugin npm registry,then remove plugin', async ({ page, mockPage }) => { test('add plugin from npm registry, then remove plugin', async ({ page, mockPage }) => {
await mockPage().goto(); await mockPage().goto();
await page.getByTestId('plugin-manager-button').click(); await page.getByTestId('plugin-manager-button').click();
await expect(page.getByLabel('sample-custom-collection-template')).not.toBeVisible(); await expect(page.getByLabel('sample-custom-collection-template')).not.toBeVisible();
@ -23,70 +23,70 @@ test.describe('add plugin in front', () => {
.getByRole('textbox') .getByRole('textbox')
.fill('@nocobase/plugin-sample-custom-collection-template'); .fill('@nocobase/plugin-sample-custom-collection-template');
await page.getByLabel('Submit').click(); await page.getByLabel('Submit').click();
//等待页面刷新结束 // wait for the page to finish refreshing
await page.waitForFunction(() => { await page.waitForFunction(() => {
const modal = document.querySelector('.ant-modal'); const modal = document.querySelector('.ant-modal');
if (modal) { if (modal) {
const computedStyle = window.getComputedStyle(modal); const computedStyle = window.getComputedStyle(modal);
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden'; return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
} }
return true; // 如果找不到modal也算作不可见 return true; // if the modal cannot be found, consider it hidden
}); });
await page.waitForLoadState('load'); await page.waitForLoadState('load');
await page.getByPlaceholder('Search plugin').fill('sample-custom-collection-template'); await page.getByPlaceholder('Search plugin').fill('sample-custom-collection-template');
await expect(page.getByLabel('sample-custom-collection-template')).toBeVisible(); await expect(page.getByLabel('sample-custom-collection-template')).toBeVisible();
//将添加的插件删除 // remove the added plugin
await page.getByLabel('sample-custom-collection-template').getByText('Remove').click(); await page.getByLabel('sample-custom-collection-template').getByText('Remove').click();
await page.getByRole('button', { name: 'Yes' }).click(); await page.getByRole('button', { name: 'Yes' }).click();
await page.waitForTimeout(300); await page.waitForTimeout(300);
//等待页面刷新结束 // wait for the page to finish refreshing
await waitForModalToBeHidden(page); await waitForModalToBeHidden(page);
await page.waitForLoadState('load'); await page.waitForLoadState('load');
await page.getByPlaceholder('Search plugin').fill('sample-custom-collection-template'); await page.getByPlaceholder('Search plugin').fill('sample-custom-collection-template');
await expect(page.getByLabel('sample-custom-collection-template')).not.toBeVisible(); await expect(page.getByLabel('sample-custom-collection-template')).not.toBeVisible();
}); });
test.skip('add plugin local upload', async ({ page, mockPage }) => {}); test.skip('add plugin from local upload', async ({ page, mockPage }) => {});
test.skip('add plugin file url', async ({ page, mockPage }) => {}); test.skip('add plugin from file URL', async ({ page, mockPage }) => {});
}); });
test.describe('remove plugin', () => { test.describe('remove plugin', () => {
test.slow(); test.slow();
test('remove plugin,then add plugin', async ({ page, mockPage }) => { test('remove plugin, then add plugin', async ({ page, mockPage }) => {
await mockPage().goto(); await mockPage().goto();
await page.getByTestId('plugin-manager-button').click(); await page.getByTestId('plugin-manager-button').click();
//hello插件默认安装未启用 // the Hello plugin is not enabled by default
await page.getByPlaceholder('Search plugin').fill('Hello'); await page.getByPlaceholder('Search plugin').fill('Hello');
await expect(page.getByLabel('Hello')).toBeVisible(); await expect(page.getByLabel('Hello')).toBeVisible();
const isActive = await page.getByLabel('Hello').getByLabel('enable').isChecked(); const isActive = await page.getByLabel('Hello').getByLabel('enable').isChecked();
expect(isActive).toBe(false); expect(isActive).toBe(false);
//将hello插件remove // remove the Hello plugin
await page.getByLabel('Hello').getByText('Remove').click(); await page.getByLabel('Hello').getByText('Remove').click();
await page.getByRole('button', { name: 'Yes' }).click(); await page.getByRole('button', { name: 'Yes' }).click();
//等待页面刷新结束 // wait for the page to finish refreshing
await waitForModalToBeHidden(page); await waitForModalToBeHidden(page);
await page.waitForLoadState('load'); await page.waitForLoadState('load');
await page.getByPlaceholder('Search plugin').fill('hello'); await page.getByPlaceholder('Search plugin').fill('hello');
await expect(page.getByLabel('Hello')).not.toBeVisible(); await expect(page.getByLabel('Hello')).not.toBeVisible();
//将删除的插件加回来 // add the removed plugin back
await page.getByRole('button', { name: 'Add new' }).click(); await page.getByRole('button', { name: 'Add new' }).click();
await page await page
.getByLabel('block-item-Input-Npm package name') .getByLabel('block-item-Input-Npm package name')
.getByRole('textbox') .getByRole('textbox')
.fill('@nocobase/plugin-sample-hello'); .fill('@nocobase/plugin-sample-hello');
await page.getByLabel('Submit').click(); await page.getByLabel('Submit').click();
//等待弹窗消失和页面刷新结束 // wait for the modal to disappear and the page to finish refreshing
await page.waitForFunction(() => { await page.waitForFunction(() => {
const modal = document.querySelector('.ant-modal'); const modal = document.querySelector('.ant-modal');
if (modal) { if (modal) {
const computedStyle = window.getComputedStyle(modal); const computedStyle = window.getComputedStyle(modal);
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden'; return computedStyle.display === 'none' || computedStyle.visibility === 'hidden';
} }
return true; // 如果找不到modal也算作不可见 return true; // if the modal cannot be found, consider it hidden
}); });
await page.waitForLoadState('load'); await page.waitForLoadState('load');
await page.getByPlaceholder('Search plugin').fill('hello'); await page.getByPlaceholder('Search plugin').fill('hello');
await expect(page.getByLabel('Hello')).toBeVisible(); await expect(page.getByLabel('Hello')).toBeVisible();
//已启用的插件不能removeACL // cannot remove enabled plugins like ACL
await page.getByPlaceholder('Search plugin').fill('ACL'); await page.getByPlaceholder('Search plugin').fill('ACL');
await expect(page.getByLabel('ACL')).toBeVisible(); await expect(page.getByLabel('ACL')).toBeVisible();
const isAclActive = await page.getByLabel('ACL').getByLabel('enable').isChecked(); const isAclActive = await page.getByLabel('ACL').getByLabel('enable').isChecked();
@ -95,7 +95,7 @@ test.describe('remove plugin', () => {
}); });
}); });
test.describe('enable & disabled plugin', () => { test.describe('enable & disable plugin', () => {
test.slow(); test.slow();
test('enable plugin', async ({ page, mockPage }) => { test('enable plugin', async ({ page, mockPage }) => {
await mockPage().goto(); await mockPage().goto();
@ -103,17 +103,17 @@ test.describe('enable & disabled plugin', () => {
await page.getByPlaceholder('Search plugin').fill('hello'); await page.getByPlaceholder('Search plugin').fill('hello');
await expect(page.getByLabel('Hello')).toBeVisible(); await expect(page.getByLabel('Hello')).toBeVisible();
await expect(page.getByLabel('Hello').getByLabel('enable')).not.toBeChecked(); await expect(page.getByLabel('Hello').getByLabel('enable')).not.toBeChecked();
//激活插件 // activate the plugin
await page.getByLabel('Hello').getByLabel('enable').click(); await page.getByLabel('Hello').getByLabel('enable').click();
await page.waitForTimeout(1000); // 等待1秒钟 await page.waitForTimeout(1000); // Wait for 1 second
//等待弹窗消失和页面刷新结束 // wait for the modal to disappear and the page to finish refreshing
await waitForModalToBeHidden(page); await waitForModalToBeHidden(page);
await page.waitForLoadState('load'); await page.waitForLoadState('load');
await page.getByPlaceholder('Search plugin').fill('hello'); await page.getByPlaceholder('Search plugin').fill('hello');
await expect(page.getByLabel('Hello').getByLabel('enable')).toBeChecked(); await expect(page.getByLabel('Hello').getByLabel('enable')).toBeChecked();
//将激活的插件禁用 // disable the activated plugin
await page.getByLabel('Hello').getByLabel('enable').click(); await page.getByLabel('Hello').getByLabel('enable').click();
//等待弹窗消失和页面刷新结束 // wait for the modal to disappear and the page to finish refreshing
await waitForModalToBeHidden(page); await waitForModalToBeHidden(page);
await page.waitForLoadState('load'); await page.waitForLoadState('load');
await expect(page.getByLabel('Hello').getByLabel('enable')).not.toBeChecked(); await expect(page.getByLabel('Hello').getByLabel('enable')).not.toBeChecked();

View File

@ -0,0 +1,101 @@
import { Page, expect, test } from '@nocobase/test/client';
import { createTable } from './utils';
test.describe('where table data selector can be added', () => {
test('popup', async ({ page, mockPage, mockRecord }) => {
await createTable({ page, mockPage, fieldName: 'manyToOne' });
// 选中一行数据之后,弹窗自动关闭,且数据被填充到关联字段中
await page.getByLabel('checkbox').click();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToOne-manyToOne')
.getByTestId('select-data-picker'),
).toHaveText(`1`);
});
});
test.describe('configure actions', () => {
test('filter & add new & delete & refresh', async ({ page, mockPage }) => {
await createTable({ page, mockPage, fieldName: 'manyToOne' });
// add buttons
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await expect(page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Refresh' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Filter' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Delete' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).toBeVisible();
// delete buttons
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await expect(page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Refresh' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Filter' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Delete' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible();
});
test('customize: bulk update', async ({ page, mockPage }) => {
await createTable({ page, mockPage, fieldName: 'manyToOne' });
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'Bulk update' }).click();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Bulk update' })).toBeVisible();
});
});
test.describe('configure actions column', () => {
test('column width', async ({ page, mockPage }) => {
await createTable({ page, mockPage, fieldName: 'manyToOne' });
await createActionColumn(page);
// 列宽度默认为 200
await expect(page.getByRole('columnheader', { name: 'Actions', exact: true })).toHaveJSProperty('offsetWidth', 200);
await page.getByText('Actions', { exact: true }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-users').hover();
await page.getByRole('menuitem', { name: 'Column width' }).click();
await expect(page.getByRole('dialog').getByText('Column width')).toBeVisible();
// 修改列宽度为 400
await page.getByRole('dialog').getByRole('spinbutton').click();
await page.getByRole('dialog').getByRole('spinbutton').fill('400');
await page.getByTestId('modal-Action.Modal-users-Column width').getByRole('button', { name: 'Submit' }).click();
// 关闭 settings 设置的下拉列表,不然获取不到宽度值
await page.getByRole('menuitem', { name: 'Column width' }).hover();
await page.mouse.move(300, 0);
await expect(page.getByRole('columnheader', { name: 'Actions', exact: true })).toHaveJSProperty('offsetWidth', 400);
});
});
async function createActionColumn(page: Page) {
await page.getByLabel('schema-initializer-TableV2.Selector-TableColumnInitializers-users').hover();
await page.getByRole('menuitem', { name: 'Action column' }).click();
}

View File

@ -0,0 +1,19 @@
import { expectSettingsMenu, test } from '@nocobase/test/client';
import { createTable } from './utils';
test.describe('table data selector schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await createTable({ page, mockPage, fieldName: 'manyToOne' });
await expectSettingsMenu({
page,
showMenu: () => showSettingsMenu(page),
supportedOptions: ['Set the data scope', 'Set default sorting rules', 'Records per page', 'Delete'],
});
});
});
async function showSettingsMenu(page) {
await page.getByLabel('block-item-CardItem-users-table-selector').hover();
await page.getByLabel('designer-schema-settings-CardItem-TableSelectorDesigner-users').hover();
}

View File

@ -0,0 +1,24 @@
import { NocoPage, Page, PageConfig, oneFormBlockWithAllAssociationFieldsAndSelectorMode } from '@nocobase/test/client';
export async function createTable({
page,
mockPage,
fieldName,
}: {
fieldName: string;
page: Page;
mockPage: (config?: PageConfig) => NocoPage;
}) {
await mockPage(oneFormBlockWithAllAssociationFieldsAndSelectorMode).goto();
// open dialog
await page
.getByLabel(`block-item-CollectionField-general-form-general.${fieldName}-${fieldName}`)
.getByTestId('select-data-picker')
.click();
// create a table block
await page.getByLabel('schema-initializer-Grid-TableSelectorInitializers-users').hover();
await page.getByRole('menuitem', { name: 'form Table' }).click();
await page.mouse.move(300, 0);
}

View File

@ -0,0 +1,45 @@
import { Page, expect, expectSettingsMenu, oneEmptyTableWithTreeCollection, test } from '@nocobase/test/client';
test.describe('tree table block schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableWithTreeCollection).goto();
await expectSettingsMenu({
page,
showMenu: () => showSettingsMenu(page),
supportedOptions: [
'Edit block title',
'Tree table',
'Enable drag and drop sorting',
'Fix block',
'Set the data scope',
'Set default sorting rules',
'Records per page',
'Connect data blocks',
'Save as template',
'Delete',
],
});
});
// TODO: 这里的问题是:使用 mockRecords 生成固定数据,但是表格中的展开按钮有时显示有时不显示,导致测试不稳定,所以先注释掉了
test('tree table', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneEmptyTableWithTreeCollection).waitForInit();
// await mockRecords('treeCollection', 30);
await nocoPage.goto();
// 默认情况下的 tree table 是开启状态,且有显示展开按钮
// await expect(page.getByLabel('Expand row').first()).toBeVisible({ timeout: 1000 * 60 });
await showSettingsMenu(page);
await expect(page.getByRole('menuitem', { name: 'Tree table' }).getByRole('switch')).toBeChecked();
await page.getByRole('menuitem', { name: 'Tree table' }).click();
await expect(page.getByRole('menuitem', { name: 'Tree table' }).getByRole('switch')).not.toBeChecked();
// await expect(page.getByLabel('Expand row')).toBeHidden();
});
});
async function showSettingsMenu(page: Page) {
await page.getByLabel('block-item-CardItem-treeCollection-table').hover();
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-treeCollection').hover();
}

View File

@ -0,0 +1,14 @@
import { expect, test } from '@nocobase/test/client';
import { T2187 } from '../templatesOfBug';
// fix https://nocobase.height.app/T-2187
test('BUG: in the Duplicate mode, the Roles field should not have a value after clicking it because it is not selected', async ({
page,
mockPage,
}) => {
await mockPage(T2187).goto();
await page.getByLabel('action-Action.Link-Duplicate-duplicate-users-table-0').click();
await expect(page.getByRole('textbox')).toHaveValue('Super Admin');
await expect(page.getByTestId('select-object-multiple')).toHaveText('');
});

View File

@ -0,0 +1,31 @@
import { expect, test } from '@nocobase/test/client';
import { T2183, T2186 } from '../templatesOfBug';
// fix https://nocobase.height.app/T-2183
test('BUG: should save conditions', async ({ page, mockPage }) => {
await mockPage(T2183).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
await page.getByRole('button', { name: 'Save conditions' }).click();
await page.reload();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
// After refreshing the browser, the set field and operator should still be visible
await expect(page.getByTestId('select-filter-field').getByText('ID')).toBeVisible();
await expect(page.getByTestId('select-filter-operator').getByText('is')).toBeVisible();
});
// fix https://nocobase.height.app/T-2186
test('BUG: the input box displayed should correspond to the field type', async ({ page, mockPage }) => {
await mockPage(T2186).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
await page.getByTestId('select-filter-field').getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
// 应该显示数字输入框
await expect(page.getByRole('spinbutton')).toBeVisible();
});

View File

@ -0,0 +1,22 @@
import { checkboxForTableRow, expect, test } from '@nocobase/test/client';
test('selects the checkbox of a table row and deletes the selected row', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(checkboxForTableRow).waitForInit();
const records = await mockRecords('t_fhdhd0nk7b9', 3);
await nocoPage.goto();
await expect(page.getByText(records[0].f_vbrlno0zej9)).toBeVisible();
await page.getByLabel('table-index-1').hover();
await page.getByRole('checkbox', { name: 'checkbox' }).click();
await page.getByLabel('action-Action-Delete-destroy-t_fhdhd0nk7b9-table').click();
// 显示确认弹窗
await expect(page.getByText('Are you sure you want to delete it?')).toBeVisible();
// 点击确认
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 确认内容已被删除
await expect(page.getByText(records[0].f_vbrlno0zej9)).not.toBeVisible();
});

View File

@ -0,0 +1,32 @@
import { expect, oneEmptyTableBlockBasedOnUsers, test } from '@nocobase/test/client';
test('actions', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableBlockBasedOnUsers).goto();
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-users').hover();
//添加按钮
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
// 向右挪动鼠标指针,用以关闭下拉列表
await page.mouse.move(300, 0);
const addNewBtn = page.getByLabel('action-Action-Add new-create-users-table');
await addNewBtn.hover();
const addNewDrag = page.getByLabel('action-Action-Add new-create-users-table').getByLabel('designer-drag');
await addNewDrag.hover();
const delBtn = page.getByLabel('action-Action-Delete-destroy-users-table');
await addNewDrag.dragTo(delBtn);
const refreshBtn = 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);
//拖拽调整排序符合预期
expect(addNew.x).toBe(max);
});
test('fields', async () => {});

View File

@ -1,7 +1,72 @@
import { expect, oneEmptyTable, test } from '@nocobase/test/client'; import { createBlockInPage, expect, oneEmptyTable, test } from '@nocobase/test/client';
test.describe('Table', () => { test.describe('where table block can be added', () => {
test('add column', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover();
await createBlockInPage(page, 'Table');
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
});
test('popup', async () => {});
});
test.describe('configure actions', () => {
test('filter & add new & delete & refresh', async ({ page, mockPage }) => {
await mockPage(oneEmptyTable).goto();
// add buttons
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await expect(page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Refresh' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Filter' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Delete' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).toBeVisible();
// delete buttons
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await expect(page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Refresh' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Filter' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Delete' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible();
});
test('customize: add record', async ({ page, mockPage }) => {
await mockPage(oneEmptyTable).goto();
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'add record' }).click();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Add record' })).toBeVisible();
});
});
test.describe('configure columns', () => {
test('action column & display collection fields & display association fields', async ({ page, mockPage }) => {
await mockPage(oneEmptyTable).goto(); await mockPage(oneEmptyTable).goto();
const configureColumnButton = page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-t_unp4scqamw9'); const configureColumnButton = page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-t_unp4scqamw9');
@ -11,10 +76,6 @@ test.describe('Table', () => {
await expect(page.getByText('Actions', { exact: true })).toBeVisible(); await expect(page.getByText('Actions', { exact: true })).toBeVisible();
await configureColumnButton.hover(); await configureColumnButton.hover();
// 滚动鼠标,以把底部的 Action column 选项显示出来
await page.getByText('Display collection fields', { exact: true }).hover();
await page.mouse.wheel(0, 300);
await expect(page.getByRole('menuitem', { name: 'Action column' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'Action column' }).getByRole('switch')).toBeChecked();
await page.getByRole('menuitem', { name: 'Action column' }).click(); await page.getByRole('menuitem', { name: 'Action column' }).click();
await expect(page.getByRole('menuitem', { name: 'Action column' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'Action column' }).getByRole('switch')).not.toBeChecked();
@ -25,19 +86,18 @@ test.describe('Table', () => {
// 再次开启 Action column // 再次开启 Action column
await configureColumnButton.hover(); await configureColumnButton.hover();
await page.mouse.wheel(0, 300);
await page.getByRole('menuitem', { name: 'Action column' }).click(); await page.getByRole('menuitem', { name: 'Action column' }).click();
await expect(page.getByRole('menuitem', { name: 'Action column' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'Action column' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);
await expect(page.getByText('Actions', { exact: true })).toBeVisible(); await expect(page.getByText('Actions', { exact: true })).toBeVisible();
// collection fields ------------------------------------------------------------- // display collection fields -------------------------------------------------------------
await configureColumnButton.hover(); await configureColumnButton.hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await page.getByRole('menuitem', { name: 'One to one (belongs to)' }).first().click(); await page.getByRole('menuitem', { name: 'One to one (belongs to)' }).first().click();
await page.getByRole('menuitem', { name: 'One to one (has one)' }).first().click(); await page.getByRole('menuitem', { name: 'One to one (has one)' }).first().click();
await page.getByRole('menuitem', { name: 'Many to one' }).first().click(); await page.getByRole('menuitem', { name: 'Many to one' }).first().click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'One to one (belongs to)' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'One to one (belongs to)' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'One to one (has one)' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'One to one (has one)' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Many to one' }).getByRole('switch')).toBeChecked(); await expect(page.getByRole('menuitem', { name: 'Many to one' }).getByRole('switch')).toBeChecked();
@ -48,11 +108,11 @@ test.describe('Table', () => {
// 点击开关,删除创建的字段 // 点击开关,删除创建的字段
await configureColumnButton.hover(); await configureColumnButton.hover();
await page.getByRole('menuitem', { name: 'ID' }).click(); await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await page.getByRole('menuitem', { name: 'One to one (belongs to)' }).first().click(); await page.getByRole('menuitem', { name: 'One to one (belongs to)' }).first().click();
await page.getByRole('menuitem', { name: 'One to one (has one)' }).first().click(); await page.getByRole('menuitem', { name: 'One to one (has one)' }).first().click();
await page.getByRole('menuitem', { name: 'Many to one' }).first().click(); await page.getByRole('menuitem', { name: 'Many to one' }).first().click();
await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'ID', exact: true }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'One to one (belongs to)' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'One to one (belongs to)' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'One to one (has one)' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'One to one (has one)' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Many to one' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'Many to one' }).getByRole('switch')).not.toBeChecked();
@ -62,7 +122,7 @@ test.describe('Table', () => {
await expect(page.getByRole('button', { name: 'One to one (has one)', exact: true })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'One to one (has one)', exact: true })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Many to one', exact: true })).not.toBeVisible(); await expect(page.getByRole('button', { name: 'Many to one', exact: true })).not.toBeVisible();
// association fields ------------------------------------------------------------- // display association fields -------------------------------------------------------------
await configureColumnButton.hover(); await configureColumnButton.hover();
await page.getByText('Display collection fields', { exact: true }).hover(); await page.getByText('Display collection fields', { exact: true }).hover();
@ -86,10 +146,11 @@ test.describe('Table', () => {
await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked(); await expect(page.getByRole('menuitem', { name: 'Nickname' }).getByRole('switch')).not.toBeChecked();
}); });
// TODO: 继承表相关测试 test.pgOnly('display inherit fields', async ({ page, mockPage }) => {});
test.pgOnly('inherit column', async ({ page, mockPage }) => {}); });
test('add column action buttons', async ({ page, mockPage, mockRecord }) => { test.describe('configure actions column', () => {
test('view & edit & delete & duplicate & customize', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTable).waitForInit(); const nocoPage = await mockPage(oneEmptyTable).waitForInit();
await mockRecord('t_unp4scqamw9'); await mockRecord('t_unp4scqamw9');
await nocoPage.goto(); await nocoPage.goto();
@ -148,7 +209,7 @@ test.describe('Table', () => {
).toBeVisible(); ).toBeVisible();
}); });
test('adjust column width', async ({ page, mockPage }) => { test('column width', async ({ page, mockPage }) => {
await mockPage(oneEmptyTable).goto(); await mockPage(oneEmptyTable).goto();
// 列宽度默认为 200 // 列宽度默认为 200
@ -166,55 +227,4 @@ test.describe('Table', () => {
await expect(page.getByRole('columnheader', { name: 'Actions', exact: true })).toHaveJSProperty('offsetWidth', 400); await expect(page.getByRole('columnheader', { name: 'Actions', exact: true })).toHaveJSProperty('offsetWidth', 400);
}); });
test('add action buttons', async ({ page, mockPage }) => {
await mockPage(oneEmptyTable).goto();
// add buttons
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await expect(page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Refresh' }).getByRole('switch')).toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Filter' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Delete' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).toBeVisible();
// delete buttons
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Filter' }).click();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Refresh' }).click();
await expect(page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Delete' }).getByRole('switch')).not.toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Refresh' }).getByRole('switch')).not.toBeChecked();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Filter' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Add new' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Delete' })).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Refresh' })).not.toBeVisible();
});
test('add custom action buttons', async ({ page, mockPage }) => {
await mockPage(oneEmptyTable).goto();
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-t_unp4scqamw9').hover();
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'Bulk update' }).click();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Bulk update' })).toBeVisible();
});
}); });

View File

@ -0,0 +1,713 @@
import {
Page,
expect,
expectSettingsMenu,
oneEmptyTableBlockWithActions,
oneEmptyTableWithTreeCollection,
oneTableBlockWithActionsAndFormBlocks,
oneTableBlockWithAddNewAndViewAndEditAndBasicFields,
test,
twoTableWithAssociationFields,
twoTableWithSameCollection,
} from '@nocobase/test/client';
test.describe('table block schema settings', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).goto();
await expectSettingsMenu({
page,
showMenu: () => showSettingsMenu(page),
supportedOptions: [
'Edit block title',
'Enable drag and drop sorting',
'Fix block',
'Set the data scope',
'Records per page',
'Connect data blocks',
'Save as template',
'Delete',
],
});
});
test('fix block', async ({ page, mockPage }) => {
await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).goto();
const tableSize = await page.getByLabel('block-item-CardItem-general-table').boundingBox();
await showSettingsMenu(page);
await page.getByRole('menuitem', { name: 'Fix block' }).click();
await expect(page.getByRole('menuitem', { name: 'Fix block' }).getByRole('switch')).toBeChecked();
// 等待页面重新渲染
await page.waitForTimeout(100);
const fixedTableSize = await page.getByLabel('block-item-CardItem-general-table').boundingBox();
expect(fixedTableSize.height).toBeGreaterThan(570);
expect(fixedTableSize.height).toBeLessThan(575);
// 取消固定
await page.getByRole('menuitem', { name: 'Fix block' }).click();
await expect(page.getByRole('menuitem', { name: 'Fix block' }).getByRole('switch')).not.toBeChecked();
// 等待页面重新渲染
await page.waitForTimeout(100);
const unfixedTableSize = await page.getByLabel('block-item-CardItem-general-table').boundingBox();
expect(unfixedTableSize.height).toBe(tableSize.height);
});
test('records per page', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
await mockRecords('general', 40);
await nocoPage.goto();
// 默认每页显示 20 条(算上顶部 head 一共 21 行)
await expect(page.getByRole('row')).toHaveCount(21);
// 底部 pagination 的显示布局:[Total 40 items] [1] [2] [20 / page]
await expect(page.locator('.ant-pagination')).toHaveText('Total 40 items1220 / page');
await showSettingsMenu(page);
await page.getByRole('menuitem', { name: 'Records per page' }).click();
await page.getByRole('option', { name: '10', exact: true }).click();
await expect(page.getByRole('row')).toHaveCount(11);
// 底部 pagination 的显示布局:[Total 40 items] [1] [2] [3] [4] [10 / page]
await expect(page.locator('.ant-pagination')).toHaveText('Total 40 items123410 / page');
});
test('enable drag and drop sorting', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
const records = await mockRecords('general', 3);
await nocoPage.goto();
await page.getByLabel('block-item-CardItem-general-table').hover();
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-general').hover();
// 默认是关闭状态
await expect(
page.getByRole('menuitem', { name: 'Enable drag and drop sorting' }).getByRole('switch'),
).not.toBeChecked();
// 开启之后,隐藏 Set default sorting rules 选项
await page.getByRole('menuitem', { name: 'Enable drag and drop sorting' }).click();
await expect(
page.getByRole('menuitem', { name: 'Enable drag and drop sorting' }).getByRole('switch'),
).toBeChecked();
await expect(page.getByRole('menuitem', { name: 'Set default sorting rules' })).toBeHidden();
// 显示出来 email 和 ID
await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-general').hover();
await page.getByRole('menuitem', { name: 'email' }).click();
await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await page.mouse.move(300, 0);
// 默认的排序
let email1 = await page.getByText(records[0].email).boundingBox();
let email2 = await page.getByText(records[1].email).boundingBox();
let email3 = await page.getByText(records[2].email).boundingBox();
expect(email1.y).toBeLessThan(email2.y);
expect(email2.y).toBeLessThan(email3.y);
// 将第二行拖动到第一行
await page
.getByLabel('table-index-2')
.getByRole('img', { name: 'menu' })
.dragTo(page.getByLabel('table-index-1').getByRole('img', { name: 'menu' }));
// 等待表格刷新
await page.waitForTimeout(1000);
email1 = await page.getByText(records[0].email).boundingBox();
email2 = await page.getByText(records[1].email).boundingBox();
email3 = await page.getByText(records[2].email).boundingBox();
expect(email2.y).toBeLessThan(email1.y);
expect(email1.y).toBeLessThan(email3.y);
});
test.describe('set the data scope', () => {
async function showDialog(page: Page) {
await page.getByLabel('block-item-CardItem-general-table').hover();
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
}
async function createColumnItem(page: Page, fieldName: string) {
await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-general').hover();
await page.getByRole('menuitem', { name: fieldName, exact: true }).click();
await page.mouse.move(300, 0);
}
test('use constants', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
await mockRecords('general', 3);
await nocoPage.goto();
await createColumnItem(page, 'ID');
await showDialog(page);
// 添加一个 ID 为 1 的条件
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').click();
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
await page.getByRole('spinbutton').click();
await page.getByRole('spinbutton').fill('1');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 被筛选之后数据只有一条(有一行是空的)
await expect(page.getByRole('row')).toHaveCount(2);
// 有一行 id 为 1 的数据
await expect(page.getByRole('cell', { name: '1', exact: true })).toBeVisible();
});
test('use variable called current user', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithActionsAndFormBlocks).waitForInit();
// Super Admin 是当前的用户的名字
await mockRecords('general', [{ singleLineText: 'Super Admin' }, {}, {}]);
await nocoPage.goto();
await createColumnItem(page, 'singleLineText');
await showDialog(page);
// 添加一个 singleLineText 为 current user 的 Nickname 的条件
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').click();
await page.getByRole('menuitemcheckbox', { name: 'singleLineText' }).click();
await page.getByLabel('variable-button').click();
await page.getByRole('menuitemcheckbox', { name: 'Current user' }).click();
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 被筛选之后数据只有一条(有一行是空的)
await expect(page.getByRole('row')).toHaveCount(2);
await expect(page.getByRole('cell', { name: 'Super Admin', exact: true })).toBeVisible();
});
});
test('set default sorting rules', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
const records = await mockRecords('general', 3);
await nocoPage.goto();
// 打开配置弹窗
await page.getByLabel('block-item-CardItem-general-table').hover();
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-general').hover();
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
// 设置一个按 ID 降序的规则
await page.getByRole('button', { name: 'plus Add sort field' }).click();
await page.getByTestId('select-single').click();
await page.getByRole('option', { name: 'ID', exact: true }).click();
await page.getByText('DESC', { exact: true }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 显示出来 email 和 ID
await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-general').hover();
await page.getByRole('menuitem', { name: 'email' }).click();
await page.getByRole('menuitem', { name: 'ID', exact: true }).click();
await page.mouse.move(300, 0);
// 规则生效后的顺序321
const email1 = await page.getByText(records[0].email).boundingBox();
const email2 = await page.getByText(records[1].email).boundingBox();
const email3 = await page.getByText(records[2].email).boundingBox();
expect(email3.y).toBeLessThan(email2.y);
expect(email2.y).toBeLessThan(email1.y);
});
test.describe('connect data blocks', () => {
test('connecting two blocks of the same collection', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(twoTableWithSameCollection).waitForInit();
const records = await mockRecords('users', 3);
await nocoPage.goto();
// 将左边的 Table 连接到右边的 Table
await page.getByLabel('block-item-CardItem-users-table').first().hover();
await page.getByRole('button', { name: 'designer-schema-settings-CardItem-TableBlockDesigner-users' }).hover();
await page.getByRole('menuitem', { name: 'Connect data blocks' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
// 点击左边 Table 的某一行,右边 Table 的数据会被筛选为当前点中行的数据
await page.getByLabel('block-item-CardItem-users-table').nth(0).getByText('Super Admin').click();
await expect(
page.getByLabel('block-item-CardItem-users-table').nth(1).getByText(records[0].nickname),
).toBeHidden();
await expect(
page.getByLabel('block-item-CardItem-users-table').nth(1).getByText(records[1].nickname),
).toBeHidden();
await expect(
page.getByLabel('block-item-CardItem-users-table').nth(1).getByText(records[2].nickname),
).toBeHidden();
await expect(page.getByLabel('block-item-CardItem-users-table').nth(1).getByText('Super Admin')).toBeVisible();
});
test('connecting two blocks connected by a relationship field', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(twoTableWithAssociationFields).waitForInit();
await mockRecords('users', 3);
await nocoPage.goto();
// 将左边的 Table 连接到右边的 Table
await page.getByLabel('block-item-CardItem-roles-table').hover();
await page.getByRole('button', { name: 'designer-schema-settings-CardItem-TableBlockDesigner-roles' }).hover();
await page.getByRole('menuitem', { name: 'Connect data blocks' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.getByRole('option', { name: 'Roles' }).click();
// 点击左边 Table 的某一行
await page.getByLabel('block-item-CardItem-roles-table').getByRole('cell', { name: 'Root', exact: true }).click();
await expect(
page
.getByLabel('block-item-CardItem-users-table')
.getByRole('row', { name: 'Member' })
.filter({ hasNotText: 'Root' }),
).toHaveCount(0, { timeout: 1000 * 10 });
await expect(page.getByLabel('block-item-CardItem-users-table').getByRole('row', { name: 'Root' })).toBeVisible();
// 再次点击,会取消筛选效果
await page.getByLabel('block-item-CardItem-roles-table').getByRole('cell', { name: 'Root', exact: true }).click();
await expect(
page
.getByLabel('block-item-CardItem-users-table')
.getByRole('row', { name: 'Member' })
.filter({ hasNotText: 'Root' }),
).toHaveCount(3, { timeout: 1000 * 10 });
await expect(page.getByLabel('block-item-CardItem-users-table').getByRole('row', { name: 'Root' })).toBeVisible();
});
test('connecting two blocks connected by a foreign key', async ({ page, mockPage, mockRecords }) => {});
});
});
test.describe('actions schema settings', () => {
test.describe('add new', () => {
const showMenu = async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Open mode', 'Popup size', 'Delete'],
});
});
test('edit button', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
await page.getByRole('menuitem', { name: 'Edit button' }).click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('1234');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByRole('button', { name: '1234' })).toBeVisible();
});
test('open mode', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
// 默认是 drawer
await expect(page.getByRole('menuitem', { name: 'Open mode' }).getByText('Drawer')).toBeVisible();
// 切换为 dialog
await page.getByRole('menuitem', { name: 'Open mode' }).click();
await page.getByRole('option', { name: 'Dialog' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
await expect(page.getByTestId('modal-Action.Container-general-Add record')).toBeVisible();
});
test('popup size', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
// 默认值 middle
await expect(page.getByRole('menuitem', { name: 'Popup size' }).getByText('Middle')).toBeVisible();
// 切换为 small
await page.getByRole('menuitem', { name: 'Popup size' }).click();
await page.getByRole('option', { name: 'Small' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
const drawerWidth =
(await page.getByTestId('drawer-Action.Container-general-Add record').boundingBox())?.width || 0;
expect(drawerWidth).toBeLessThanOrEqual(400);
await page.getByLabel('drawer-Action.Container-general-Add record-mask').click();
// 切换为 large
await showMenu(page);
await page.getByRole('menuitem', { name: 'Popup size' }).click();
await page.getByRole('option', { name: 'Large' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
const drawerWidth2 =
(await page.getByTestId('drawer-Action.Container-general-Add record').boundingBox())?.width || 0;
expect(drawerWidth2).toBeGreaterThanOrEqual(800);
});
test('delete', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await showMenu(page);
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByRole('button', { name: 'Add new' })).toBeHidden();
});
});
test.describe('bulk delete', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Delete' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
},
supportedOptions: ['Edit button', 'Delete'],
});
});
});
test.describe('filter', () => {
const showMenu = async (page: Page) => {
await page.getByRole('button', { name: 'Filter' }).hover();
await page.getByLabel('designer-schema-settings-Filter.Action-Filter.Action.Designer-general').hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: [
'Edit button',
'Delete',
'Many to one',
'One to many',
'Single select',
'ID',
'Created at',
'Last updated at',
'Created by',
'Last updated by',
],
});
});
});
test.describe('delete', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Delete-destroy-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Linkage rules', 'Delete'],
});
});
test('edit button', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await showMenu(page);
await page.getByRole('menuitem', { name: 'Edit button' }).click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('Delete record');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('action-Action.Link-Delete record-destroy-general-table-0')).toBeVisible();
});
});
test.describe('edit', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit-update-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});
test.describe('refresh', () => {
test('supported options', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Refresh' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
},
supportedOptions: ['Edit button', 'Delete'],
});
});
});
test.describe('view', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-View-view-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
test('linkage rules', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndBasicFields).waitForInit();
await mockRecord('general');
await nocoPage.goto();
const openLinkageRules = async () => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').hover();
await page
.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' })
.hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
};
// 设置第一组规则 --------------------------------------------------------------------------
await openLinkageRules();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
// 添加一个条件ID 等于 1
await page.getByText('Add condition', { exact: true }).click();
await page.getByTestId('select-filter-field').click();
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
await page.getByRole('spinbutton').click();
await page.getByRole('spinbutton').fill('1');
// action: 禁用按钮
await page.getByText('Add property').click();
await page.getByLabel('block-item-ArrayCollapse-general').click();
await page.getByTestId('select-linkage-properties').getByLabel('Search').click();
await page.getByRole('option', { name: 'Disabled' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('action-Action.Link-View record-view-general-table-0')).toHaveAttribute(
'disabled',
'',
);
// 设置第二组规则 --------------------------------------------------------------------------
await openLinkageRules();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.locator('.ant-collapse-header').nth(1).getByRole('img', { name: 'right' }).click();
// 添加一个条件ID 等于 1
await page.getByRole('tabpanel').getByText('Add condition', { exact: true }).click();
await page.getByRole('button', { name: 'Search Select field' }).getByLabel('Search').click();
await page.getByRole('menuitemcheckbox', { name: 'ID', exact: true }).click();
await page.getByRole('spinbutton').click();
await page.getByRole('spinbutton').fill('1');
// action: 使按钮可用
await page.getByRole('tabpanel').getByText('Add property').click();
await page.getByRole('combobox', { name: 'Search' }).click();
await page.getByRole('option', { name: 'Enabled' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 后面的 action 会覆盖前面的
await expect(page.getByLabel('action-Action.Link-View record-view-general-table-0')).not.toHaveAttribute(
'disabled',
'',
);
});
});
test.describe('popup', () => {
const addSomeCustomActions = async (page: Page) => {
// 先删除掉之前的 actions
await page.getByRole('button', { name: 'Actions' }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-general').hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
// 再增加两个自定义的 actions
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByRole('menuitem', { name: 'Update record' }).click();
};
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Popup-customize:popup-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await addSomeCustomActions(page);
await showMenu(page);
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});
test.describe('duplicate', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Linkage rules', 'Duplicate mode', 'Open mode', 'Popup size', 'Delete'],
});
});
});
test.describe('update record', () => {
const addSomeCustomActions = async (page: Page) => {
// 先删除掉之前的 actions
await page.getByRole('button', { name: 'Actions' }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-general').hover();
await page.getByRole('menuitem', { name: 'View' }).click();
await page.getByRole('menuitem', { name: 'Edit' }).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
// 再增加两个自定义的 actions
await page.getByRole('menuitem', { name: 'Customize' }).hover();
await page.getByRole('menuitem', { name: 'Popup' }).click();
await page.getByRole('menuitem', { name: 'Update record' }).click();
};
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Update record-customize:update-general-table-0').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await addSomeCustomActions(page);
await showMenu(page);
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: [
'Edit button',
'Linkage rules',
'Assign field values',
'After successful submission',
'Delete',
],
});
});
});
test.describe('add child', () => {
const showMenu = async (page: Page) => {
await page.getByLabel('action-Action.Link-Add child-create-treeCollection-table-0').hover();
await page.getByLabel('designer-schema-settings-Action.Link-Action.Designer-tree').hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneEmptyTableWithTreeCollection).waitForInit();
await nocoPage.goto();
// 添加一行数据
// TODO: 使用 mockRecord 为 tree 表添加一行数据无效
await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-treeCollection').hover();
await page.getByRole('menuitem', { name: 'Add new' }).click();
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel('schema-initializer-Grid-CreateFormBlockInitializers-treeCollection').hover();
await page.getByRole('menuitem', { name: 'form Form' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-ActionBar-CreateFormActionInitializers-treeCollection').hover();
await page.getByRole('menuitem', { name: 'Submit' }).click();
await page.mouse.move(300, 0);
await page.getByRole('button', { name: 'Submit' }).click();
// 添加 add child 按钮
await page.getByRole('button', { name: 'Actions' }).hover();
await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-tree').hover();
await page.getByRole('menuitem', { name: 'Add child' }).click();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
});
});
test.describe('add record', () => {
const showMenu = async (page: Page) => {
await page.getByRole('button', { name: 'Add record' }).hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-Action.Designer-general' }).hover();
};
test('supported options', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyTableBlockWithActions).goto();
await expectSettingsMenu({
page,
showMenu: () => showMenu(page),
supportedOptions: ['Edit button', 'Open mode', 'Popup size', 'Delete'],
});
});
});
});
async function showSettingsMenu(page) {
await page.getByLabel('block-item-CardItem-general-table').hover();
await page.getByLabel('designer-schema-settings-CardItem-TableBlockDesigner-general').hover();
}

Some files were not shown because too many files have changed in this diff Show More