feat: run e2e with pro plugins (#3890)

* feat: run e2e with pro plugins

* fix: name

* chore: fix typo

* fix: make e2e pass

* chore: only run pro e2e

* Revert "chore: only run pro e2e"

This reverts commit 9beff480c125ecbf221a61d69e09ca6e98119816.

* chore: increase timeout

* feat: support to set maxDepth

* chore: set default maxDepth to 1

* chore: fix e2e to make pass

* chore: add test for BlockSchemaToolbar

* chore: split files to avoid slowing down testing

* chore: make e2e more stable

* chore: make e2e more stable

* refactor: e2e

---------

Co-authored-by: Zeke Zhang <958414905@qq.com>
This commit is contained in:
chenos 2024-04-15 12:40:53 +08:00 committed by GitHub
parent 2a4c356bc2
commit 31d68f91e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1061 additions and 920 deletions

View File

@ -42,7 +42,19 @@ jobs:
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Checkout pro-plugins
uses: actions/checkout@v3
with:
repository: nocobase/pro-plugins
ref: main
path: packages/pro-plugins
ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }}
- name: Set variables
run: |
APPEND_PRESET_LOCAL_PLUGINS=$(find ./packages/pro-plugins/@nocobase -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sed 's/^plugin-//' | tr '\n' ',' | sed 's/,$//')
echo "var2=$APPEND_PRESET_LOCAL_PLUGINS" >> $GITHUB_OUTPUT
id: vars
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v3
with:
@ -51,7 +63,6 @@ jobs:
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
@ -78,4 +89,5 @@ jobs:
DB_USER: nocobase
DB_PASSWORD: password
DB_DATABASE: nocobase
timeout-minutes: 180
APPEND_PRESET_LOCAL_PLUGINS: ${{ steps.vars.outputs.var2 }}
timeout-minutes: 240

View File

@ -85,6 +85,7 @@ async function appReady() {
async function runApp(options = {}) {
console.log('installing...');
await run('nocobase', ['install', '-f']);
await run('nocobase', ['pm', 'enable-all']);
if (await isPortReachable(process.env.APP_PORT)) {
console.log('app started');
return;
@ -232,6 +233,7 @@ module.exports = (cli) => {
e2e.command('reinstall-app').action(async (options) => {
await run('nocobase', ['install', '-f'], options);
await run('nocobase', ['pm2', 'enable-all']);
});
e2e.command('install-deps').action(async () => {

View File

@ -9,7 +9,7 @@ import { useSchemaTemplate } from '../../schema-templates';
export const BlockSchemaToolbar = (props) => {
const { t } = useTranslation();
const cm = useCollectionManager();
let { name: currentCollectionName, title: currentCollectionTitle } = useCollection();
let { name: currentCollectionName, title: currentCollectionTitle } = useCollection() || {};
const template = useSchemaTemplate();
const { association } = useDataBlockProps() || {};
const compile = useCompile();
@ -40,13 +40,13 @@ export const BlockSchemaToolbar = (props) => {
return <SchemaToolbar title={toolbarTitle} {...props} />;
};
function getCollectionTitle(arg0: {
export function getCollectionTitle(arg: {
collectionTitle: string;
collectionName: string;
associationField: any;
compile: any;
}) {
const { collectionTitle, collectionName, associationField, compile } = arg0;
const { collectionTitle, collectionName, associationField, compile } = arg;
if (associationField) {
return `${compile(collectionTitle || collectionName)} > ${compile(
@ -54,5 +54,5 @@ function getCollectionTitle(arg0: {
)}`;
}
return collectionTitle || collectionName;
return compile(collectionTitle || collectionName);
}

View File

@ -0,0 +1,38 @@
import { getCollectionTitle } from '../BlockSchemaToolbar';
describe('getCollectionTitle', () => {
it('should return collectionTitle if associationField is falsy', () => {
const arg = {
collectionTitle: 'Collection Title',
collectionName: 'Collection Name',
associationField: null,
compile: vi.fn((value) => value),
};
const result = getCollectionTitle(arg);
expect(result).toBe('Collection Title');
expect(arg.compile).toHaveBeenCalledWith('Collection Title');
});
it('should return compiled collectionTitle and associationField title if associationField is truthy', () => {
const arg = {
collectionTitle: 'Collection Title',
collectionName: 'Collection Name',
associationField: {
uiSchema: {
title: 'Association Field Title',
},
name: 'Association Field Name',
},
compile: vi.fn((value) => `Compiled: ${value}`),
};
const result = getCollectionTitle(arg);
expect(result).toBe('Compiled: Collection Title > Compiled: Association Field Title');
expect(arg.compile).toHaveBeenCalledTimes(2);
expect(arg.compile).toHaveBeenCalledWith('Collection Title');
expect(arg.compile).toHaveBeenCalledWith('Association Field Title');
});
});

View File

@ -24,7 +24,7 @@ test.describe('where single data details block can be added', () => {
clearBlockTemplates,
}) => {
const nocoPage = await mockPage(T3848).waitForInit();
await mockRecord('example');
await mockRecord('example', 2);
await nocoPage.goto();
// 1.打开弹窗

View File

@ -11,7 +11,7 @@ import { T2200, T2614, T2615, T2845, T2993 } 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 mockRecord('general', 2);
await nocoPage.goto();
// 为关系字段选中一个值
@ -30,7 +30,7 @@ test.describe('display association fields', () => {
test('subform: should display correctly', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneSubformWithMultiLevelAssociationFields).waitForInit();
await mockRecord('general');
await mockRecord('general', 3);
await nocoPage.goto();
// 为子表单中的关系字段选中一个值
@ -52,7 +52,7 @@ test.describe('display association fields', () => {
// 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 mockRecord('T2615', 2);
await nocoPage.goto();
// 1. 新增表单中应该显示关系字段的数据
@ -91,7 +91,7 @@ test.describe('display association fields', () => {
test('should load association data of subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2845).waitForInit();
// 和 T2615 使用一样的数据表结构
const record = await mockRecord('T2615');
const record = await mockRecord('T2615', 3);
await nocoPage.goto();
// 1. 新增表单中应该显示关系字段的数据
@ -139,7 +139,7 @@ test.describe('display association fields', () => {
// 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 mockRecord('T2614', 2);
await nocoPage.goto();
// 查看详情
@ -167,7 +167,7 @@ test.describe('display association fields', () => {
// https://nocobase.height.app/T-2993
test('should load association data of sub details', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2993).waitForInit();
const record = await mockRecord('T2993');
const record = await mockRecord('T2993', 3);
await nocoPage.goto();
await page.getByLabel('action-Action-Add new-create-').click();
@ -186,7 +186,7 @@ test.describe('display association fields', () => {
test.describe('association fields', () => {
test('subform: load association fields', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableSubformWithMultiLevelAssociationFields).waitForInit();
const record = await mockRecord('general');
const record = await mockRecord('general', 4);
await nocoPage.goto();
// 查看详情
@ -229,7 +229,7 @@ test.describe('association fields', () => {
test('subtable: load association fields', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableSubtableWithMultiLevelAssociationFields).waitForInit();
const record = await mockRecord('general');
const record = await mockRecord('general', 2);
await nocoPage.goto();
// 查看详情

View File

@ -3,7 +3,7 @@ import { expect, formBlockDefaultValueTemplate, test } from '@nocobase/test/e2e'
test.describe('variables with default value', () => {
test('current form', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(formBlockDefaultValueTemplate).waitForInit();
await mockRecord('general');
await mockRecord('general', 2);
await nocoPage.goto();
await page.getByRole('button', { name: 'Add new' }).click();

View File

@ -2,6 +2,7 @@ import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneEmptyTableBlockWithActions,
oneEmptyTableWithTreeCollection,
oneTableBlockWithActionsAndFormBlocks,
@ -232,7 +233,7 @@ test.describe('table block schema settings', () => {
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);
const records = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
// 将左边的 Table 连接到右边的 Table
@ -257,7 +258,7 @@ test.describe('table block schema settings', () => {
test('connecting two blocks connected by a relationship field', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(twoTableWithAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
// 将左边的 Table 连接到右边的 Table

View File

@ -46,6 +46,17 @@ export default (app: Application) => {
}
});
pm.command('enable-all')
.ipc()
.preload()
.action(async () => {
try {
await app.pm.enable('*');
} catch (error) {
throw new PluginCommandError(`Failed to enable plugin`, { cause: error });
}
});
pm.command('enable')
.ipc()
.preload()

View File

@ -75,7 +75,7 @@ export class Locale {
getResources(lang: string) {
const resources = {};
const names = this.app.pm.getAliases();
const names = this.app.pm.getPlugins().keys();
for (const name of names) {
try {
const p = this.app.pm.get(name);

View File

@ -1,3 +1,4 @@
import Topo from '@hapi/topo';
import { CleanOptions, Collection, SyncOptions } from '@nocobase/database';
import { importModule, isURL } from '@nocobase/utils';
import { fsExists } from '@nocobase/utils/plugin-symlink';
@ -328,6 +329,9 @@ export class PluginManager {
if (options.name) {
this.pluginAliases.set(options.name, instance);
}
if (options.packageName) {
this.pluginAliases.set(options.packageName, instance);
}
if (insert && options.name) {
await this.repository.updateOrCreate({
values: {
@ -478,8 +482,27 @@ export class PluginManager {
});
}
private sort(names: string | string[]) {
const pluginNames = _.castArray(names);
if (pluginNames.length === 1) {
return pluginNames;
}
const sorter = new Topo.Sorter<string>();
for (const pluginName of pluginNames) {
const plugin = this.get(pluginName);
const peerDependencies = Object.keys(plugin.options?.packageJson?.peerDependencies || {});
sorter.add(pluginName, { after: peerDependencies, group: plugin.options?.packageName || pluginName });
}
return sorter.nodes;
}
async enable(name: string | string[]) {
const pluginNames = _.castArray(name);
let pluginNames = name;
if (name === '*') {
const items = await this.repository.find();
pluginNames = items.map((item: any) => item.name);
}
pluginNames = this.sort(pluginNames);
this.app.log.debug(`enabling plugin ${pluginNames.join(',')}`);
this.app.setMaintainingMessage(`enabling plugin ${pluginNames.join(',')}`);
const toBeUpdated = [];
@ -492,9 +515,17 @@ export class PluginManager {
continue;
}
await this.app.emitAsync('beforeEnablePlugin', pluginName);
await plugin.beforeEnable();
plugin.enabled = true;
toBeUpdated.push(pluginName);
try {
await plugin.beforeEnable();
plugin.enabled = true;
toBeUpdated.push(pluginName);
} catch (error) {
if (name === '*') {
this.app.log.error(error.message);
} else {
throw error;
}
}
}
if (toBeUpdated.length === 0) {
return;
@ -509,10 +540,10 @@ export class PluginManager {
});
try {
await this.app.reload();
this.app.log.debug(`syncing database in enable plugin ${pluginNames.join(',')}...`);
this.app.setMaintainingMessage(`syncing database in enable plugin ${pluginNames.join(',')}...`);
this.app.log.debug(`syncing database in enable plugin ${toBeUpdated.join(',')}...`);
this.app.setMaintainingMessage(`syncing database in enable plugin ${toBeUpdated.join(',')}...`);
await this.app.db.sync();
for (const pluginName of pluginNames) {
for (const pluginName of toBeUpdated) {
const plugin = this.get(pluginName);
if (!plugin.installed) {
this.app.log.debug(`installing plugin ${pluginName}...`);
@ -529,7 +560,7 @@ export class PluginManager {
installed: true,
},
});
for (const pluginName of pluginNames) {
for (const pluginName of toBeUpdated) {
const plugin = this.get(pluginName);
this.app.log.debug(`emit afterEnablePlugin event...`);
await plugin.afterEnable();
@ -884,7 +915,7 @@ export class PluginManager {
async list(options: any = {}) {
const { locale = 'en-US', isPreset = false } = options;
return Promise.all(
[...this.getAliases()]
[...this.getPlugins().keys()]
.map((name) => {
const plugin = this.get(name);
if (!isPreset && plugin.options.isPreset) {

View File

@ -206,11 +206,21 @@ interface ExtendUtils {
mockCollection: (collectionSetting: CollectionSetting) => Promise<any>;
/**
* collection
* @param collectionName
* @param data
* @returns
*/
mockRecord: <T = any>(collectionName: string, data?: any) => Promise<T>;
mockRecord: {
/**
* @param collectionName
* @param data
* @param maxDepth - 4
*/
<T = any>(collectionName: string, data?: any, maxDepth?: number): Promise<T>;
/**
* @param collectionName
* @param maxDepth - 4
*/
<T = any>(collectionName: string, maxDepth?: number): Promise<T>;
};
/**
* collection
*/
@ -218,13 +228,15 @@ interface ExtendUtils {
/**
* @param collectionName -
* @param count -
* @param maxDepth - 4
*/
<T = any>(collectionName: string, count?: number): Promise<T[]>;
<T = any>(collectionName: string, count?: number, maxDepth?: number): Promise<T[]>;
/**
* @param collectionName -
* @param data -
* @param maxDepth - 4
*/
<T = any>(collectionName: string, data?: any[]): Promise<T[]>;
<T = any>(collectionName: string, data?: any[], maxDepth?: number): Promise<T[]>;
};
/**
* 使 mockCollections
@ -408,18 +420,28 @@ const _test = base.extend<ExtendUtils>({
},
mockRecords: async ({ page }, use) => {
const mockRecords = async (collectionName: string, count: any = 3, data?: any) => {
let maxDepth: number;
if (_.isNumber(data)) {
maxDepth = data;
data = undefined;
}
if (_.isArray(count)) {
data = count;
count = data.length;
}
return createRandomData(collectionName, count, data);
return createRandomData(collectionName, count, data, maxDepth);
};
await use(mockRecords);
},
mockRecord: async ({ page }, use) => {
const mockRecord = async (collectionName: string, data?: any) => {
const result = await createRandomData(collectionName, 1, data);
const mockRecord = async (collectionName: string, data?: any, maxDepth?: any) => {
if (_.isNumber(data)) {
maxDepth = data;
data = undefined;
}
const result = await createRandomData(collectionName, 1, data, maxDepth);
return result[0];
};
@ -861,7 +883,7 @@ const generateFakerData = (collectionSetting: CollectionSetting) => {
return result;
};
const createRandomData = async (collectionName: string, count = 10, data?: any) => {
const createRandomData = async (collectionName: string, count = 10, data?: any, maxDepth?: number) => {
const api = await request.newContext({
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
});
@ -869,10 +891,13 @@ const createRandomData = async (collectionName: string, count = 10, data?: any)
const state = await api.storageState();
const headers = getHeaders(state);
const result = await api.post(`/api/${collectionName}:mock?count=${count}`, {
headers,
data,
});
const result = await api.post(
`/api/${collectionName}:mock?count=${count}&maxDepth=${_.isNumber(maxDepth) ? maxDepth : 1}`,
{
headers,
data,
},
);
if (!result.ok()) {
throw new Error(await result.text());
@ -993,3 +1018,13 @@ export const createBlockInPage = async (page: Page, name: string) => {
await page.mouse.move(300, 0);
};
export const mockUserRecordsWithoutDepartments = (mockRecords: ExtendUtils['mockRecords'], count: number) => {
return mockRecords(
'users',
Array.from({ length: count }).map(() => ({
departments: null,
mainDepartment: null,
})),
);
};

View File

@ -1,5 +1,6 @@
import {
expectInitializerMenu,
mockUserRecordsWithoutDepartments,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
} from '@nocobase/test/e2e';
@ -7,7 +8,7 @@ import {
test.describe('form item & create form', () => {
test('configure fields', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await expectInitializerMenu({
@ -24,7 +25,7 @@ test.describe('form item & create form', () => {
test.describe('form item & edit form', () => {
test('configure fields', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();

View File

@ -2,6 +2,7 @@ import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneFilterFormBlockWithAllAssociationFields,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
@ -11,7 +12,7 @@ import { createColumnItem, showSettingsMenu, testPattern } from '../../utils';
test.describe('form item & create form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await expectSettingsMenu({
@ -44,7 +45,7 @@ test.describe('form item & create form', () => {
test('set default value', async ({ page, mockPage, mockRecord, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -67,7 +68,7 @@ test.describe('form item & create form', () => {
page,
gotoPage: async () => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
records = await mockRecords('users', 3);
records = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
},
openDialog: async () => {
@ -108,7 +109,7 @@ test.describe('form item & create form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -154,7 +155,7 @@ test.describe('form item & create form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -180,7 +181,7 @@ test.describe('form item & create form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -201,7 +202,7 @@ test.describe('form item & create form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -222,7 +223,7 @@ test.describe('form item & create form', () => {
test.describe('form item & edit form', () => {
test('supported options', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();
@ -262,7 +263,7 @@ test.describe('form item & edit form', () => {
record = (
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -308,7 +309,7 @@ test.describe('form item & edit form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const { recordsOfUser } = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -355,7 +356,7 @@ test.describe('form item & edit form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -382,7 +383,7 @@ test.describe('form item & edit form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -404,7 +405,7 @@ test.describe('form item & edit form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();

View File

@ -2,6 +2,7 @@ import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneFilterFormBlockWithAllAssociationFields,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
@ -11,7 +12,7 @@ import { createColumnItem, showSettingsMenu, testPattern } from '../../utils';
test.describe('form item & create form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await expectSettingsMenu({
@ -42,7 +43,7 @@ test.describe('form item & create form', () => {
test('set default value', async ({ page, mockPage, mockRecord, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -66,7 +67,7 @@ test.describe('form item & create form', () => {
gotoPage: async () => {
records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -112,7 +113,7 @@ test.describe('form item & create form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -158,7 +159,7 @@ test.describe('form item & create form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -184,7 +185,7 @@ test.describe('form item & create form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -205,7 +206,7 @@ test.describe('form item & create form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -226,7 +227,7 @@ test.describe('form item & create form', () => {
test.describe('form item & edit form', () => {
test('supported options', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();
@ -264,7 +265,7 @@ test.describe('form item & edit form', () => {
record = (
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -310,7 +311,7 @@ test.describe('form item & edit form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -356,7 +357,7 @@ test.describe('form item & edit form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -383,7 +384,7 @@ test.describe('form item & edit form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -405,7 +406,7 @@ test.describe('form item & edit form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();

View File

@ -1,803 +0,0 @@
import {
Page,
expect,
expectSettingsMenu,
oneFilterFormBlockWithAllAssociationFields,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
} from '@nocobase/test/e2e';
import { createColumnItem, showSettingsMenu, testDefaultValue, testPattern } from '../../utils';
test.describe('form item & create form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
},
supportedOptions: [
'Edit field title',
'Display title',
'Edit description',
'Required',
'Set default value',
'Set the data scope',
'Set default sorting rules',
'Field component',
'Quick create',
'Allow multiple',
'Pattern',
'Title field',
'Delete',
],
});
});
test('set default value', async ({ page, mockPage, mockRecords }) => {
let recordsOfUser;
await testDefaultValue({
page,
gotoPage: async () => {
recordsOfUser = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
},
openDialog: () =>
(async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page),
closeDialog: () => page.getByLabel('drawer-Action.Container-general-Add record-mask').click(),
showMenu: () =>
(async (page: Page, 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();
})(page, 'manyToMany'),
supportVariables: ['Constant', 'Current user', 'Date variables', 'Current form'],
inputConstantValue: async () => {
await page.getByLabel('block-item-VariableInput-').getByTestId('select-object-multiple').click();
await page.getByRole('option', { name: String(recordsOfUser[0].id), exact: true }).click();
await page.getByRole('option', { name: String(recordsOfUser[1].id), exact: true }).click();
await page.getByRole('option', { name: String(recordsOfUser[2].id), exact: true }).click();
},
expectConstantValue: async () => {
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany'),
).toHaveText(`manyToMany:${recordsOfUser.map((record) => record.id).join('')}`);
},
});
});
test('pattern', async ({ page, mockPage, mockRecords }) => {
let records;
await testPattern({
page,
gotoPage: async () => {
records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
},
openDialog: () =>
(async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page),
showMenu: () =>
(async (page: Page, 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();
})(page, 'manyToMany'),
expectEditable: async () => {
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple')
.click();
await page.getByRole('option', { name: String(records[0].id), exact: true }).click();
await page.getByRole('option', { name: String(records[1].id), exact: true }).click();
await page.getByRole('option', { name: String(records[2].id), exact: true }).click();
},
expectReadonly: async () => {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple'),
).toHaveClass(/ant-select-disabled/);
// 在这里等待一下,防止因闪烁导致下面的断言失败
await page.waitForTimeout(100);
},
expectEasyReading: async () => {
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany'),
).toHaveText(`manyToMany:${records.map((record) => record.id).join(',')}`);
},
});
});
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
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(String(records[0].id));
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
await expect(page.getByTestId('select-filter-field')).toHaveText('ID');
await expect(page.getByRole('spinbutton')).toHaveValue(String(records[0].id));
await page.getByRole('button', { name: 'Cancel', exact: true }).click();
// 数据应该被过滤了
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple')
.click();
await expect(page.getByRole('option', { name: String(records[0].id), exact: true })).toBeVisible();
await expect(page.getByRole('option')).toHaveCount(1);
});
test('set default sorting rules', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
// 配置
await page.getByRole('button', { name: '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();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
await expect(page.getByRole('dialog').getByTestId('select-single')).toHaveText('ID');
});
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项
await expect(page.getByRole('option', { name: 'Select', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Record picker', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-table', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form(Popover)', exact: true })).toBeVisible();
// 选择 Record picker
await page.getByRole('option', { name: 'Record picker', exact: true }).click();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-data-picker'),
).toBeVisible();
// 选择 Sub-table
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-table', exact: true }).click();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByLabel('schema-initializer-AssociationField.SubTable-table:configureColumns-users'),
).toBeVisible();
// 选择 Sub-form
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-form', exact: true }).click();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByLabel('schema-initializer-Grid-form:configureFields-users'),
).toBeVisible();
// 选择 Sub-form(Popover)
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
// 加上这个延迟会使测试更稳定
await page.waitForTimeout(100);
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-form(Popover)', exact: true }).click();
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByRole('img', { name: 'edit' })
.click();
await expect(page.getByTestId('popover-CollectionField-general')).toBeVisible();
});
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Quick create' })).toBeVisible();
});
test('allow multiple', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Allow multiple' })).toBeVisible();
});
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Title field' })).toBeVisible();
});
});
test.describe('form item & edit form', () => {
test('supported options', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockRecords('general', 1);
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
},
supportedOptions: [
'Edit field title',
'Display title',
'Edit description',
'Required',
'Set the data scope',
'Set default sorting rules',
'Field component',
'Quick create',
'Allow multiple',
'Pattern',
'Title field',
'Delete',
],
unsupportedOptions: ['Set default value'],
});
});
test('pattern', async ({ page, mockPage, mockRecords }) => {
let record;
await testPattern({
page,
gotoPage: async () => {
record = (
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords)
).record;
},
openDialog: () =>
(async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page),
showMenu: () =>
(async (page: Page, 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();
})(page, 'manyToMany'),
expectEditable: async () => {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple'),
).toHaveText(`${record.manyToMany.map((item) => item.id).join('')}`);
},
expectReadonly: async () => {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple'),
).toHaveClass(/ant-select-disabled/);
// 在这里等待一下,防止因闪烁导致下面的断言失败
await page.waitForTimeout(100);
},
expectEasyReading: async () => {
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany'),
).toHaveText(`manyToMany:${record.manyToMany.map((item) => item.id).join(',')}`);
},
});
});
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const { recordsOfUser } = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
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(String(recordsOfUser[0].id));
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
await expect(page.getByTestId('select-filter-field')).toHaveText('ID');
await expect(page.getByRole('spinbutton')).toHaveValue(String(recordsOfUser[0].id));
await page.getByRole('button', { name: 'Cancel', exact: true }).click();
// 数据应该被过滤了
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple')
.click();
await expect(page.getByRole('option', { name: String(recordsOfUser[0].id), exact: true })).toBeVisible();
});
test('set default sorting rules', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
// 配置
await page.getByRole('button', { name: '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();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
await expect(page.getByRole('dialog').getByTestId('select-single')).toHaveText('ID');
});
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项
await expect(page.getByRole('option', { name: 'Select', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Record picker', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-table', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form(Popover)', exact: true })).toBeVisible();
});
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Quick create' })).toBeVisible();
});
test('allow multiple', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Allow multiple' })).toBeVisible();
});
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Title field' })).toBeVisible();
});
});
test.describe('form item & view form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('general', 1);
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
},
supportedOptions: [
'Edit field title',
'Display title',
'Edit tooltip',
'Field component',
'Enable link',
'Title field',
'Delete',
],
unsupportedOptions: ['Set default value'],
});
});
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return record;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项
await expect(page.getByRole('option', { name: 'Title', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Tag', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-details', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-table', exact: true })).toBeVisible();
// 切换到 Sub-details应该显示 Initializer 按钮
await page.getByRole('option', { name: 'Sub-details', exact: true }).click();
await page.mouse.move(300, 0);
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByLabel('schema-initializer-Grid-form:')
.first(),
).toBeVisible();
});
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return record;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Title field' })).toBeVisible();
});
test('enable link', async ({ page, mockPage, mockRecords }) => {
// test.fail();
const record = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return record;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(page);
// 初始状态是一个可点击的链接
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.locator('a')
.filter({ hasText: record.manyToMany[0].id }),
).toBeVisible();
await (async (page: Page, 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();
})(page, 'manyToMany');
// 默认情况下是开启状态
await expect(page.getByRole('menuitem', { name: 'Enable link' }).getByRole('switch')).toBeChecked();
await page.getByRole('menuitem', { name: 'Enable link' }).click();
await expect(page.getByRole('menuitem', { name: 'Enable link' }).getByRole('switch')).not.toBeChecked();
// 应变为非链接状态
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.locator('a')
.filter({ hasText: record.manyToMany[0].id }),
).not.toBeVisible();
// 再次开启链接状态
await page.getByRole('menuitem', { name: 'Enable link' }).click();
await expect(page.getByRole('menuitem', { name: 'Enable link' }).getByRole('switch')).toBeChecked();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.locator('a')
.filter({ hasText: record.manyToMany[0].id }),
).toBeVisible();
});
});
test.describe('form item & filter form', () => {
test('supported options', async ({ page, mockPage }) => {
const nocoPage = await mockPage(oneFilterFormBlockWithAllAssociationFields).waitForInit();
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CollectionField-general-filter-form-general.manyToMany-manyToMany').hover();
await page.getByRole('button', { name: 'designer-schema-settings-CollectionField' }).hover();
},
supportedOptions: [
'Edit field title',
'Edit description',
'Set the data scope',
'Field component',
'Title field',
'Delete',
],
});
});
});
test.describe('table column & table', () => {
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await createColumnItem(page, 'manyToMany');
await showSettingsMenu(page, 'manyToMany');
},
supportedOptions: [
'Custom column title',
'Column width',
'Enable link',
'Title field',
'Field component',
'Delete',
],
});
});
});

View File

@ -0,0 +1,349 @@
import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
} from '@nocobase/test/e2e';
import { testDefaultValue, testPattern } from '../../utils';
test.describe('form item & create form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
},
supportedOptions: [
'Edit field title',
'Display title',
'Edit description',
'Required',
'Set default value',
'Set the data scope',
'Set default sorting rules',
'Field component',
'Quick create',
'Allow multiple',
'Pattern',
'Title field',
'Delete',
],
});
});
test('set default value', async ({ page, mockPage, mockRecords }) => {
let recordsOfUser;
await testDefaultValue({
page,
gotoPage: async () => {
recordsOfUser = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
},
openDialog: () =>
(async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page),
closeDialog: () => page.getByLabel('drawer-Action.Container-general-Add record-mask').click(),
showMenu: () =>
(async (page: Page, 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();
})(page, 'manyToMany'),
supportVariables: ['Constant', 'Current user', 'Date variables', 'Current form'],
inputConstantValue: async () => {
await page.getByLabel('block-item-VariableInput-').getByTestId('select-object-multiple').click();
await page.getByRole('option', { name: String(recordsOfUser[0].id), exact: true }).click();
await page.getByRole('option', { name: String(recordsOfUser[1].id), exact: true }).click();
await page.getByRole('option', { name: String(recordsOfUser[2].id), exact: true }).click();
},
expectConstantValue: async () => {
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany'),
).toHaveText(`manyToMany:${recordsOfUser.map((record) => record.id).join('')}`);
},
});
});
test('pattern', async ({ page, mockPage, mockRecords }) => {
let records;
await testPattern({
page,
gotoPage: async () => {
records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
},
openDialog: () =>
(async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page),
showMenu: () =>
(async (page: Page, 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();
})(page, 'manyToMany'),
expectEditable: async () => {
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple')
.click();
await page.getByRole('option', { name: String(records[0].id), exact: true }).click();
await page.getByRole('option', { name: String(records[1].id), exact: true }).click();
await page.getByRole('option', { name: String(records[2].id), exact: true }).click();
},
expectReadonly: async () => {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple'),
).toHaveClass(/ant-select-disabled/);
// 在这里等待一下,防止因闪烁导致下面的断言失败
await page.waitForTimeout(100);
},
expectEasyReading: async () => {
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany'),
).toHaveText(`manyToMany:${records.map((record) => record.id).join(',')}`);
},
});
});
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
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(String(records[0].id));
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
await expect(page.getByTestId('select-filter-field')).toHaveText('ID');
await expect(page.getByRole('spinbutton')).toHaveValue(String(records[0].id));
await page.getByRole('button', { name: 'Cancel', exact: true }).click();
// 数据应该被过滤了
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple')
.click();
await expect(page.getByRole('option', { name: String(records[0].id), exact: true })).toBeVisible();
await expect(page.getByRole('option')).toHaveCount(1);
});
test('set default sorting rules', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
// 配置
await page.getByRole('button', { name: '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();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
await expect(page.getByRole('dialog').getByTestId('select-single')).toHaveText('ID');
});
test('field component', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await page.getByRole('button', { name: 'Add new' }).click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项
await expect(page.getByRole('option', { name: 'Select', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Record picker', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-table', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form(Popover)', exact: true })).toBeVisible();
// 选择 Record picker
await page.getByRole('option', { name: 'Record picker', exact: true }).click();
await page.mouse.move(300, 0);
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-data-picker'),
).toBeVisible();
// 选择 Sub-table
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-table', exact: true }).click();
await page.mouse.move(300, 0);
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByLabel('schema-initializer-AssociationField.SubTable-table:configureColumns-users'),
).toBeVisible();
// 选择 Sub-form
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-form', exact: true }).click();
await page.mouse.move(300, 0);
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByLabel('schema-initializer-Grid-form:configureFields-users'),
).toBeVisible();
// 选择 Sub-form(Popover)
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
// 加上这个延迟会使测试更稳定
await page.waitForTimeout(100);
await page.getByRole('menuitem', { name: 'Field component' }).click();
await page.getByRole('option', { name: 'Sub-form(Popover)', exact: true }).click();
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByRole('img', { name: 'edit' })
.click();
await page.mouse.move(300, 0);
await expect(page.getByTestId('popover-CollectionField-general')).toBeVisible();
});
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Quick create' })).toBeVisible();
});
test('allow multiple', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Allow multiple' })).toBeVisible();
});
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByRole('button', { name: 'Add new' }).click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Title field' })).toBeVisible();
});
});

View File

@ -0,0 +1,263 @@
import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
} from '@nocobase/test/e2e';
import { testPattern } from '../../utils';
test.describe('form item & edit form', () => {
test('supported options', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
},
supportedOptions: [
'Edit field title',
'Display title',
'Edit description',
'Required',
'Set the data scope',
'Set default sorting rules',
'Field component',
'Quick create',
'Allow multiple',
'Pattern',
'Title field',
'Delete',
],
unsupportedOptions: ['Set default value'],
});
});
test('pattern', async ({ page, mockPage, mockRecords }) => {
let record;
await testPattern({
page,
gotoPage: async () => {
record = (
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords)
).record;
},
openDialog: () =>
(async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page),
showMenu: () =>
(async (page: Page, 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();
})(page, 'manyToMany'),
expectEditable: async () => {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple'),
).toHaveText(`${record.manyToMany.map((item) => item.id).join('')}`);
},
expectReadonly: async () => {
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple'),
).toHaveClass(/ant-select-disabled/);
// 在这里等待一下,防止因闪烁导致下面的断言失败
await page.waitForTimeout(100);
},
expectEasyReading: async () => {
await expect(
page.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany'),
).toHaveText(`manyToMany:${record.manyToMany.map((item) => item.id).join(',')}`);
},
});
});
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const { recordsOfUser } = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
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(String(recordsOfUser[0].id));
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set the data scope' }).click();
await expect(page.getByTestId('select-filter-field')).toHaveText('ID');
await expect(page.getByRole('spinbutton')).toHaveValue(String(recordsOfUser[0].id));
await page.getByRole('button', { name: 'Cancel', exact: true }).click();
// 数据应该被过滤了
await page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByTestId('select-object-multiple')
.click();
await expect(page.getByRole('option', { name: String(recordsOfUser[0].id), exact: true })).toBeVisible();
});
test('set default sorting rules', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
// 配置
await page.getByRole('button', { name: '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();
// 再次打开弹窗,设置的值应该还在
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Set default sorting rules' }).click();
await expect(page.getByRole('dialog').getByTestId('select-single')).toHaveText('ID');
});
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项
await expect(page.getByRole('option', { name: 'Select', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Record picker', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-table', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-form(Popover)', exact: true })).toBeVisible();
});
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Quick create' })).toBeVisible();
});
test('allow multiple', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return { record, recordsOfUser };
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Allow multiple' })).toBeVisible();
});
test('title field', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();
await page.getByLabel('action-Action.Link-Edit record-update-general-table-0').click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
await expect(page.getByRole('menuitem', { name: 'Title field' })).toBeVisible();
});
});

View File

@ -0,0 +1,144 @@
import {
Page,
expect,
expectSettingsMenu,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
} from '@nocobase/test/e2e';
test.describe('form item & view form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('general', 1);
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
await page.getByLabel(`block-item-CollectionField-general-form-general.manyToMany-manyToMany`).hover();
await page
.getByLabel(`designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToMany`)
.hover();
},
supportedOptions: [
'Edit field title',
'Display title',
'Edit tooltip',
'Field component',
'Enable link',
'Title field',
'Delete',
],
unsupportedOptions: ['Set default value'],
});
});
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return record;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await page.getByRole('menuitem', { name: 'Field component' }).click();
// 断言支持的选项
await expect(page.getByRole('option', { name: 'Title', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Tag', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-details', exact: true })).toBeVisible();
await expect(page.getByRole('option', { name: 'Sub-table', exact: true })).toBeVisible();
// 切换到 Sub-details应该显示 Initializer 按钮
await page.getByRole('option', { name: 'Sub-details', exact: true }).click();
await page.mouse.move(300, 0);
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.getByLabel('schema-initializer-Grid-form:')
.first(),
).toBeVisible();
});
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return record;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(page);
await (async (page: Page, 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();
})(page, 'manyToMany');
await expect(page.getByRole('menuitem', { name: 'Title field' })).toBeVisible();
});
test('enable link', async ({ page, mockPage, mockRecords }) => {
// test.fail();
const record = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
return record;
})(mockPage, mockRecords);
await (async (page: Page) => {
await page.getByLabel('action-Action.Link-View record-view-general-table-0').click();
})(page);
// 初始状态是一个可点击的链接
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.locator('a')
.filter({ hasText: record.manyToMany[0].id }),
).toBeVisible();
await (async (page: Page, 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();
})(page, 'manyToMany');
// 默认情况下是开启状态
await expect(page.getByRole('menuitem', { name: 'Enable link' }).getByRole('switch')).toBeChecked();
await page.getByRole('menuitem', { name: 'Enable link' }).click();
await expect(page.getByRole('menuitem', { name: 'Enable link' }).getByRole('switch')).not.toBeChecked();
// 应变为非链接状态
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.locator('a')
.filter({ hasText: record.manyToMany[0].id }),
).not.toBeVisible();
// 再次开启链接状态
await page.getByRole('menuitem', { name: 'Enable link' }).click();
await expect(page.getByRole('menuitem', { name: 'Enable link' }).getByRole('switch')).toBeChecked();
await expect(
page
.getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany')
.locator('a')
.filter({ hasText: record.manyToMany[0].id }),
).toBeVisible();
});
});

View File

@ -0,0 +1,24 @@
import { expectSettingsMenu, oneFilterFormBlockWithAllAssociationFields, test } from '@nocobase/test/e2e';
test.describe('form item & filter form', () => {
test('supported options', async ({ page, mockPage }) => {
const nocoPage = await mockPage(oneFilterFormBlockWithAllAssociationFields).waitForInit();
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('block-item-CollectionField-general-filter-form-general.manyToMany-manyToMany').hover();
await page.getByRole('button', { name: 'designer-schema-settings-CollectionField' }).hover();
},
supportedOptions: [
'Edit field title',
'Edit description',
'Set the data scope',
'Field component',
'Title field',
'Delete',
],
});
});
});

View File

@ -0,0 +1,30 @@
import {
expectSettingsMenu,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
} from '@nocobase/test/e2e';
import { createColumnItem, showSettingsMenu } from '../../utils';
test.describe('table column & table', () => {
test('supported options', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecord('general');
await nocoPage.goto();
await expectSettingsMenu({
page,
showMenu: async () => {
await createColumnItem(page, 'manyToMany');
await showSettingsMenu(page, 'manyToMany');
},
supportedOptions: [
'Custom column title',
'Column width',
'Enable link',
'Title field',
'Field component',
'Delete',
],
});
});
});

View File

@ -2,6 +2,7 @@ import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneFilterFormBlockWithAllAssociationFields,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
@ -12,7 +13,7 @@ import { T3377 } from './templatesOfBug';
test.describe('form item & create form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await expectSettingsMenu({
@ -48,7 +49,7 @@ test.describe('form item & create form', () => {
gotoPage: async () => {
records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -101,7 +102,7 @@ test.describe('form item & create form', () => {
gotoPage: async () => {
records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -145,7 +146,7 @@ test.describe('form item & create form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -191,7 +192,7 @@ test.describe('form item & create form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -217,7 +218,7 @@ test.describe('form item & create form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -238,7 +239,7 @@ test.describe('form item & create form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -259,7 +260,7 @@ test.describe('form item & create form', () => {
test.describe('form item & edit form', () => {
test('supported options', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();
@ -297,7 +298,7 @@ test.describe('form item & edit form', () => {
record = (
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -343,7 +344,7 @@ test.describe('form item & edit form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const { recordsOfUser } = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -390,7 +391,7 @@ test.describe('form item & edit form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -417,7 +418,7 @@ test.describe('form item & edit form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -439,7 +440,7 @@ test.describe('form item & edit form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();

View File

@ -2,6 +2,7 @@ import {
Page,
expect,
expectSettingsMenu,
mockUserRecordsWithoutDepartments,
oneFilterFormBlockWithAllAssociationFields,
oneTableBlockWithAddNewAndViewAndEditAndAssociationFields,
test,
@ -11,7 +12,7 @@ import { createColumnItem, showSettingsMenu, testPattern } from '../../utils';
test.describe('form item & create form', () => {
test('supported options', async ({ page, mockPage, mockRecords }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
await expectSettingsMenu({
@ -43,7 +44,7 @@ test.describe('form item & create form', () => {
test('set default value', async ({ page, mockPage, mockRecord, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -66,7 +67,7 @@ test.describe('form item & create form', () => {
gotoPage: async () => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -111,7 +112,7 @@ test.describe('form item & create form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const records = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -157,7 +158,7 @@ test.describe('form item & create form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -184,7 +185,7 @@ test.describe('form item & create form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -205,7 +206,7 @@ test.describe('form item & create form', () => {
test('allow multiple', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -225,7 +226,7 @@ test.describe('form item & create form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
await nocoPage.goto();
return recordsOfUser;
@ -246,7 +247,7 @@ test.describe('form item & create form', () => {
test.describe('form item & edit form', () => {
test('supported options', async ({ page, mockRecords, mockPage }) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
await mockRecords('users', 3);
await mockUserRecordsWithoutDepartments(mockRecords, 3);
await mockRecords('general', 1);
await nocoPage.goto();
@ -285,7 +286,7 @@ test.describe('form item & edit form', () => {
record = (
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -331,7 +332,7 @@ test.describe('form item & edit form', () => {
test('Set the data scope', async ({ page, mockPage, mockRecords }) => {
const { record } = await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -378,7 +379,7 @@ test.describe('form item & edit form', () => {
test('field component', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -406,7 +407,7 @@ test.describe('form item & edit form', () => {
test('quick create', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -428,7 +429,7 @@ test.describe('form item & edit form', () => {
test('allow multiple', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();
@ -449,7 +450,7 @@ test.describe('form item & edit form', () => {
test('title field', async ({ page, mockPage, mockRecords }) => {
await (async (mockPage, mockRecords) => {
const nocoPage = await mockPage(oneTableBlockWithAddNewAndViewAndEditAndAssociationFields).waitForInit();
const recordsOfUser = await mockRecords('users', 3);
const recordsOfUser = await mockUserRecordsWithoutDepartments(mockRecords, 3);
const record = (await mockRecords('general', 1))[0];
await nocoPage.goto();

View File

@ -87,21 +87,24 @@ test.describe('configure actions', () => {
test('add new action in kanban', async ({ page, mockPage }) => {
const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit();
await nocoPage.goto();
await page.getByLabel('schema-initializer-ActionBar-kanban:configureActions-general').click();
await page.getByLabel('schema-initializer-ActionBar-kanban:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch').click();
//按钮正常显示
await expect(page.getByLabel('action-Action-Add ')).toBeVisible();
//添加数据
await page.getByLabel('action-Action-Add new-create-general-kanban').click();
await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').click();
await page.getByLabel('schema-initializer-Grid-popup:addNew:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'form Form' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-form:configureFields-general').click();
await page.getByLabel('schema-initializer-Grid-form:configureFields-general').hover();
await page.getByRole('menuitem', { name: 'Single Select' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('block-item-CollectionField-').locator('.ant-select').click();
await page.getByRole('option', { name: 'option1' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-ActionBar-createForm:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Submit' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('action-Action-Submit-submit-general-form').click();
await page.getByLabel('block-item-Kanban.Card-general-kanban').hover();
await page.getByLabel('designer-schema-initializer-Kanban.Card-Kanban.Card.Designer-general').hover();

View File

@ -2,13 +2,13 @@ import Database, { Collection, Repository } from '@nocobase/database';
import { CollectionRepository } from '@nocobase/plugin-collection-manager';
import { InstallOptions, Plugin } from '@nocobase/server';
import { merge, uid } from '@nocobase/utils';
import { promises as fs } from 'fs';
import _ from 'lodash';
import path from 'path';
import { Client } from 'pg';
import collectionTemplates from './collection-templates';
import * as fieldInterfaces from './field-interfaces';
import { mockAttachment } from './field-interfaces';
import { Client } from 'pg';
import path from 'path';
import { promises as fs } from 'fs';
export class PluginMockCollectionsServer extends Plugin {
async load() {
@ -190,7 +190,7 @@ export class PluginMockCollectionsServer extends Plugin {
this.app.resourcer.registerActions({
mock: async (ctx, next) => {
const { resourceName } = ctx.action;
const { values, count = 10 } = ctx.action.params;
const { values, count = 10, maxDepth = 4 } = ctx.action.params;
const mockCollectionData = async (collectionName, count = 1, depth = 0, maxDepth = 4) => {
const collection = ctx.db.getCollection(collectionName) as Collection;
const items = await Promise.all(
@ -224,7 +224,7 @@ export class PluginMockCollectionsServer extends Plugin {
if (Array.isArray(values)) {
size = values.length;
}
const data = await mockCollectionData(resourceName, size);
const data = await mockCollectionData(resourceName, size, 0, Number(maxDepth));
// ctx.body = {
// values: (Array.isArray(data) ? data : [data]).map((item, index) => {
// if (Array.isArray(values)) {

View File

@ -2,7 +2,6 @@ import Database from '@nocobase/database';
import PluginMultiAppManager from '@nocobase/plugin-multi-app-manager';
import { Application, AppSupervisor, Plugin } from '@nocobase/server';
import lodash from 'lodash';
import { resolve } from 'path';
const subAppFilteredPlugins = ['multi-app-share-collection', 'multi-app-manager'];
const unSyncPlugins = ['localization-management'];
@ -235,13 +234,6 @@ export class MultiAppShareCollectionPlugin extends Plugin {
return;
}
await this.importCollections(resolve(__dirname, 'collections'));
// this.db.addMigrations({
// namespace: 'multi-app-share-collection',
// directory: resolve(__dirname, './migrations'),
// });
this.app.resourcer.registerActionHandlers({
'applications:shareCollections': async (ctx, next) => {
const { filterByTk, values } = ctx.action.params;
@ -262,9 +254,13 @@ export class MultiAppShareCollectionPlugin extends Plugin {
schema: appName,
};
const plugins = [...mainApp.pm.getAliases()].filter(
(name) => name !== 'multi-app-manager' && name !== 'multi-app-share-collection',
);
const plugins = [...mainApp.pm.getPlugins().values()]
.filter(
(plugin) =>
plugin?.options?.packageName !== '@nocobase/plugin-multi-app-manager' &&
plugin?.options?.packageName !== '@nocobase/plugin-multi-app-share-collection',
)
.map((plugin) => plugin.name);
return {
database: lodash.merge(databaseOptions, {