mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-11-29 18:58:26 +08:00
Merge branch 'main' into next
This commit is contained in:
commit
74ef97343b
25
.github/PULL_REQUEST_TEMPLATE/bug_fix.md
vendored
25
.github/PULL_REQUEST_TEMPLATE/bug_fix.md
vendored
@ -1,25 +0,0 @@
|
|||||||
## Description
|
|
||||||
|
|
||||||
### Steps to reproduce
|
|
||||||
|
|
||||||
<!-- Clear steps to reproduce the bug. -->
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
|
|
||||||
<!--- Describe what the expected behavior should be when the code is executed without the bug. -->
|
|
||||||
|
|
||||||
### Actual behavior
|
|
||||||
|
|
||||||
<!-- Describe what actually happens when the code is executed with the bug. -->
|
|
||||||
|
|
||||||
## Related issues
|
|
||||||
|
|
||||||
<!-- Include any related issues or previous bug reports related to this bug. -->
|
|
||||||
|
|
||||||
## Reason
|
|
||||||
|
|
||||||
<!-- Explain what caused the bug to occur. -->
|
|
||||||
|
|
||||||
## Solution
|
|
||||||
|
|
||||||
<!-- Describe solution to the bug clearly and consciously. -->
|
|
28
.github/PULL_REQUEST_TEMPLATE/feature.md
vendored
28
.github/PULL_REQUEST_TEMPLATE/feature.md
vendored
@ -1,28 +0,0 @@
|
|||||||
## Description
|
|
||||||
|
|
||||||
<!-- Describe the new feature or modification to an existing feature clearly and consciously. -->
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
<!-- Explain the reason for adding or modifying this feature. -->
|
|
||||||
|
|
||||||
## Key changes
|
|
||||||
|
|
||||||
<!-- Provide a technically detailed description of the key changes made. -->
|
|
||||||
|
|
||||||
- Frontend
|
|
||||||
- Backend
|
|
||||||
|
|
||||||
## Test plan
|
|
||||||
|
|
||||||
### Suggestions
|
|
||||||
|
|
||||||
<!-- Provide any suggestions or recommendations for improvements in the testing plan. -->
|
|
||||||
|
|
||||||
### Underlying risk
|
|
||||||
|
|
||||||
<!-- Identify any potential risks or issues that may arise from the new feature or modification. -->
|
|
||||||
|
|
||||||
## Showcase
|
|
||||||
|
|
||||||
<!-- Including any screenshots of the new feature or modification. -->
|
|
51
.github/pull_request_template.md
vendored
51
.github/pull_request_template.md
vendored
@ -1,24 +1,39 @@
|
|||||||
<!-- Note -->
|
<!--
|
||||||
<!-- This is a template for submitting a new feature.
|
First of all, thank you for your contribution!
|
||||||
Use the bug fix template if you're submitting a bug fix pull request by adding `template=bug_fix.md` to your pull request URL. -->
|
For bug fixes or other non-feature modifications, please base your branch on the main branch.
|
||||||
|
For new features or API modifications, please make sure your branch is based on the next branch.
|
||||||
|
Thank you!
|
||||||
|
-->
|
||||||
|
|
||||||
# Description
|
### This is a ...
|
||||||
<!-- Describe the new feature or modification to an existing feature clearly and consciously. -->
|
- [ ] New feature
|
||||||
|
- [ ] Bug fix
|
||||||
|
- [ ] Others
|
||||||
|
|
||||||
# Motivation
|
### Motivation
|
||||||
<!-- Explain the reason for adding or modifying this feature. -->
|
<!-- Please explain the reason of the changes made in this PR. -->
|
||||||
|
|
||||||
# Key changes
|
### Description
|
||||||
<!-- Provide a technically detailed description of the key changes made. -->
|
<!--
|
||||||
- Frontend
|
Please describe the key changes made in this PR clearly and concisely,
|
||||||
- Backend
|
mention any potential risks,
|
||||||
|
and provide some testing suggestions.
|
||||||
|
-->
|
||||||
|
|
||||||
# Test plan
|
### Showcase
|
||||||
## Suggestions
|
<!-- Including any screenshots of the changes. -->
|
||||||
<!-- Provide any suggestions or recommendations for improvements in the testing plan. -->
|
|
||||||
|
|
||||||
## Underlying risk
|
### Changelog
|
||||||
<!-- Identify any potential risks or issues that may arise from the new feature or modification. -->
|
|
||||||
|
|
||||||
# Showcase
|
| Language | Changelog |
|
||||||
<!-- Including any screenshots of the new feature or modification. -->
|
| ---------- | --------- |
|
||||||
|
| 🇺🇸 English | |
|
||||||
|
| 🇨🇳 Chinese | |
|
||||||
|
|
||||||
|
### Checklists
|
||||||
|
- [ ] All changes have been self-tested and work as expected
|
||||||
|
- [ ] Test cases are updated/provided or not needed
|
||||||
|
- [ ] Doc is updated/provided or not needed
|
||||||
|
- [ ] Component demo is updated/provided or not needed
|
||||||
|
- [ ] Changelog is provided or not needed
|
||||||
|
- [ ] Request a code review if it is necessary
|
||||||
|
@ -97,14 +97,15 @@ export class ApprovalTriggerNode {
|
|||||||
configureFieldsButton: Locator;
|
configureFieldsButton: Locator;
|
||||||
configureActionsButton: Locator;
|
configureActionsButton: Locator;
|
||||||
saveDraftSwitch: Locator;
|
saveDraftSwitch: Locator;
|
||||||
|
preloadAssociationsDropDown: Locator;
|
||||||
submitButton: Locator;
|
submitButton: Locator;
|
||||||
cancelButton: Locator;
|
cancelButton: Locator;
|
||||||
addNodeButton: Locator;
|
addNodeButton: Locator;
|
||||||
constructor(page: Page, triggerName: string, collectionName: string) {
|
constructor(page: Page, triggerName: string, collectionName: string) {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
this.node = page.getByText('TriggeraConfigure');
|
this.node = page.getByLabel(`Trigger-${triggerName}`);
|
||||||
this.nodeTitle = page.locator('textarea').filter({ hasText: triggerName });
|
this.nodeTitle = page.getByLabel(`Trigger-${triggerName}`).getByRole('textbox');
|
||||||
this.nodeConfigure = page.getByRole('button', { name: 'Configure' });
|
this.nodeConfigure = page.getByLabel(`Trigger-${triggerName}`).getByRole('button', { name: 'Configure' });
|
||||||
this.collectionDropDown = page
|
this.collectionDropDown = page
|
||||||
.getByLabel('block-item-DataSourceCollectionCascader-workflows-Collection')
|
.getByLabel('block-item-DataSourceCollectionCascader-workflows-Collection')
|
||||||
.locator('.ant-select-selection-search-input');
|
.locator('.ant-select-selection-search-input');
|
||||||
@ -121,6 +122,7 @@ export class ApprovalTriggerNode {
|
|||||||
`schema-initializer-ActionBar-ApprovalApplyAddActionButton-${collectionName}`,
|
`schema-initializer-ActionBar-ApprovalApplyAddActionButton-${collectionName}`,
|
||||||
);
|
);
|
||||||
this.saveDraftSwitch = page.getByRole('menuitem', { name: 'Save draft' }).getByRole('switch');
|
this.saveDraftSwitch = page.getByRole('menuitem', { name: 'Save draft' }).getByRole('switch');
|
||||||
|
this.preloadAssociationsDropDown = page.getByTestId('select-field-Preload associations');
|
||||||
this.submitButton = page.getByLabel('action-Action-Submit-workflows');
|
this.submitButton = page.getByLabel('action-Action-Submit-workflows');
|
||||||
this.cancelButton = page.getByLabel('action-Action-Cancel-workflows');
|
this.cancelButton = page.getByLabel('action-Action-Cancel-workflows');
|
||||||
this.addNodeButton = this.addNodeButton = page.getByLabel('add-button', { exact: true });
|
this.addNodeButton = this.addNodeButton = page.getByLabel('add-button', { exact: true });
|
||||||
@ -133,10 +135,13 @@ export class ApprovalPassthroughModeNode {
|
|||||||
nodeTitle: Locator;
|
nodeTitle: Locator;
|
||||||
nodeConfigure: Locator;
|
nodeConfigure: Locator;
|
||||||
addAssigneesButton: Locator;
|
addAssigneesButton: Locator;
|
||||||
|
addSelectAssigneesMenu: Locator;
|
||||||
|
addQueryAssigneesMenu: Locator;
|
||||||
assigneesDropDown: Locator;
|
assigneesDropDown: Locator;
|
||||||
OrRadio: Locator;
|
OrRadio: Locator;
|
||||||
AndRadio: Locator;
|
AndRadio: Locator;
|
||||||
votingRadio: Locator;
|
votingRadio: Locator;
|
||||||
|
votingThresholdEditBox: Locator;
|
||||||
parallellyRadio: Locator;
|
parallellyRadio: Locator;
|
||||||
sequentiallyRadio: Locator;
|
sequentiallyRadio: Locator;
|
||||||
goToconfigureButton: Locator;
|
goToconfigureButton: Locator;
|
||||||
@ -161,17 +166,22 @@ export class ApprovalPassthroughModeNode {
|
|||||||
.getByLabel(`Approval-${nodeName}`, { exact: true })
|
.getByLabel(`Approval-${nodeName}`, { exact: true })
|
||||||
.getByRole('button', { name: 'Configure' });
|
.getByRole('button', { name: 'Configure' });
|
||||||
this.addAssigneesButton = page.getByRole('button', { name: 'plus Add assignee' });
|
this.addAssigneesButton = page.getByRole('button', { name: 'plus Add assignee' });
|
||||||
|
this.addSelectAssigneesMenu = page.getByRole('button', { name: 'Select assignees' });
|
||||||
|
this.addQueryAssigneesMenu = page.getByRole('button', { name: 'Query assignees' });
|
||||||
this.assigneesDropDown = page.getByTestId('select-single');
|
this.assigneesDropDown = page.getByTestId('select-single');
|
||||||
this.OrRadio = page.getByLabel('Or', { exact: true });
|
this.OrRadio = page.getByLabel('Or', { exact: true });
|
||||||
this.AndRadio = page.getByLabel('And', { exact: true });
|
this.AndRadio = page.getByLabel('And', { exact: true });
|
||||||
this.votingRadio = page.getByLabel('Voting', { exact: true });
|
this.votingRadio = page.getByLabel('Voting', { exact: true });
|
||||||
|
this.votingThresholdEditBox = page
|
||||||
|
.getByLabel('block-item-NegotiationConfig-workflows-Negotiation mode')
|
||||||
|
.getByRole('spinbutton');
|
||||||
this.parallellyRadio = page.getByLabel('Parallelly', { exact: true });
|
this.parallellyRadio = page.getByLabel('Parallelly', { exact: true });
|
||||||
this.sequentiallyRadio = page.getByLabel('Sequentially', { exact: true });
|
this.sequentiallyRadio = page.getByLabel('Sequentially', { exact: true });
|
||||||
this.goToconfigureButton = page.getByRole('button', { name: 'Go to configure' });
|
this.goToconfigureButton = page.getByRole('button', { name: 'Go to configure' });
|
||||||
this.addBlockButton = page.getByLabel('schema-initializer-Grid-ApprovalProcessAddBlockButton-workflows');
|
this.addBlockButton = page.getByLabel('schema-initializer-Grid-ApprovalProcessAddBlockButton-workflows');
|
||||||
this.addDetailsMenu = page.getByRole('menuitem', { name: 'Details' });
|
this.addDetailsMenu = page.getByRole('menuitem', { name: 'Details' });
|
||||||
this.detailsConfigureFieldsButton = page.getByLabel(
|
this.detailsConfigureFieldsButton = page.getByLabel(
|
||||||
`schema-initializer-Grid-ReadPrettyFormItemInitializers-${collectionName}`,
|
`schema-initializer-Grid-details:configureFields-${collectionName}`,
|
||||||
);
|
);
|
||||||
this.addActionsMenu = page.getByRole('menuitem', { name: 'Actions' }).getByRole('switch');
|
this.addActionsMenu = page.getByRole('menuitem', { name: 'Actions' }).getByRole('switch');
|
||||||
this.actionsConfigureFieldsButton = page.getByLabel('schema-initializer-Grid-FormItemInitializers-approvalRecords');
|
this.actionsConfigureFieldsButton = page.getByLabel('schema-initializer-Grid-FormItemInitializers-approvalRecords');
|
||||||
@ -188,6 +198,83 @@ export class ApprovalPassthroughModeNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ApprovalBranchModeNode {
|
||||||
|
readonly page: Page;
|
||||||
|
node: Locator;
|
||||||
|
nodeTitle: Locator;
|
||||||
|
nodeConfigure: Locator;
|
||||||
|
addAssigneesButton: Locator;
|
||||||
|
addSelectAssigneesMenu: Locator;
|
||||||
|
addQueryAssigneesMenu: Locator;
|
||||||
|
assigneesDropDown: Locator;
|
||||||
|
OrRadio: Locator;
|
||||||
|
AndRadio: Locator;
|
||||||
|
votingRadio: Locator;
|
||||||
|
votingThresholdEditBox: Locator;
|
||||||
|
parallellyRadio: Locator;
|
||||||
|
sequentiallyRadio: Locator;
|
||||||
|
goToconfigureButton: Locator;
|
||||||
|
addBlockButton: Locator;
|
||||||
|
addDetailsMenu: Locator;
|
||||||
|
detailsConfigureFieldsButton: Locator;
|
||||||
|
addActionsMenu: Locator;
|
||||||
|
actionsConfigureFieldsButton: Locator;
|
||||||
|
actionsConfigureActionsButton: Locator;
|
||||||
|
addApproveButton: Locator;
|
||||||
|
addRejectButton: Locator;
|
||||||
|
addReturnButton: Locator;
|
||||||
|
addNodeResult: Locator;
|
||||||
|
submitButton: Locator;
|
||||||
|
cancelButton: Locator;
|
||||||
|
addNodeButton: Locator;
|
||||||
|
addReturnBranchNodeButton: Locator;
|
||||||
|
addRejectBranchNodeButton: Locator;
|
||||||
|
addApproveBranchNodeButton: Locator;
|
||||||
|
endOnRejectCheckbox: Locator;
|
||||||
|
constructor(page: Page, nodeName: string, collectionName: string) {
|
||||||
|
this.page = page;
|
||||||
|
this.node = page.getByLabel(`Approval-${nodeName}`, { exact: true });
|
||||||
|
this.nodeTitle = page.getByLabel(`Approval-${nodeName}`, { exact: true }).getByRole('textbox');
|
||||||
|
this.nodeConfigure = page
|
||||||
|
.getByLabel(`Approval-${nodeName}`, { exact: true })
|
||||||
|
.getByRole('button', { name: 'Configure' });
|
||||||
|
this.addAssigneesButton = page.getByRole('button', { name: 'plus Add assignee' });
|
||||||
|
this.addSelectAssigneesMenu = page.getByRole('button', { name: 'Select assignees' });
|
||||||
|
this.addQueryAssigneesMenu = page.getByRole('button', { name: 'Query assignees' });
|
||||||
|
this.assigneesDropDown = page.getByTestId('select-single');
|
||||||
|
this.OrRadio = page.getByLabel('Or', { exact: true });
|
||||||
|
this.AndRadio = page.getByLabel('And', { exact: true });
|
||||||
|
this.votingRadio = page.getByLabel('Voting', { exact: true });
|
||||||
|
this.votingThresholdEditBox = page
|
||||||
|
.getByLabel('block-item-NegotiationConfig-workflows-Negotiation mode')
|
||||||
|
.getByRole('spinbutton');
|
||||||
|
this.parallellyRadio = page.getByLabel('Parallelly', { exact: true });
|
||||||
|
this.sequentiallyRadio = page.getByLabel('Sequentially', { exact: true });
|
||||||
|
this.goToconfigureButton = page.getByRole('button', { name: 'Go to configure' });
|
||||||
|
this.addBlockButton = page.getByLabel('schema-initializer-Grid-ApprovalProcessAddBlockButton-workflows');
|
||||||
|
this.addDetailsMenu = page.getByRole('menuitem', { name: 'Details' });
|
||||||
|
this.detailsConfigureFieldsButton = page.getByLabel(
|
||||||
|
`schema-initializer-Grid-details:configureFields-${collectionName}`,
|
||||||
|
);
|
||||||
|
this.addActionsMenu = page.getByRole('menuitem', { name: 'Actions' }).getByRole('switch');
|
||||||
|
this.actionsConfigureFieldsButton = page.getByLabel('schema-initializer-Grid-FormItemInitializers-approvalRecords');
|
||||||
|
this.actionsConfigureActionsButton = page.getByLabel(
|
||||||
|
'schema-initializer-ActionBar-ApprovalProcessAddActionButton-approvalRecords',
|
||||||
|
);
|
||||||
|
this.addApproveButton = page.getByRole('menuitem', { name: 'Approve' }).getByRole('switch');
|
||||||
|
this.addRejectButton = page.getByRole('menuitem', { name: 'Reject' }).getByRole('switch');
|
||||||
|
this.addReturnButton = page.getByRole('menuitem', { name: 'Return' }).getByRole('switch');
|
||||||
|
this.addNodeResult = page.getByRole('menuitem', { name: 'Node result right' });
|
||||||
|
this.submitButton = page.getByLabel('action-Action-Submit-workflows');
|
||||||
|
this.cancelButton = page.getByLabel('action-Action-Cancel-workflows');
|
||||||
|
this.addNodeButton = page.getByLabel(`add-button-calculation-${nodeName}`, { exact: true });
|
||||||
|
this.addReturnBranchNodeButton = page.getByLabel(`add-button-approval-${nodeName}-1`);
|
||||||
|
this.addApproveBranchNodeButton = page.getByLabel(`add-button-approval-${nodeName}-2`);
|
||||||
|
this.addRejectBranchNodeButton = page.getByLabel(`add-button-approval-${nodeName}--1`);
|
||||||
|
this.endOnRejectCheckbox = page.getByLabel('End the workflow after');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ScheduleTriggerNode {
|
export class ScheduleTriggerNode {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
node: Locator;
|
node: Locator;
|
||||||
@ -656,4 +743,5 @@ export default module.exports = {
|
|||||||
ConditionBranchNode,
|
ConditionBranchNode,
|
||||||
SQLNode,
|
SQLNode,
|
||||||
ParallelBranchNode,
|
ParallelBranchNode,
|
||||||
|
ApprovalBranchModeNode,
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { request, Page } from '@nocobase/test/e2e';
|
import { request, Browser } from '@nocobase/test/e2e';
|
||||||
|
|
||||||
const PORT = process.env.APP_PORT || 20000;
|
const PORT = process.env.APP_PORT || 20000;
|
||||||
const APP_BASE_URL = process.env.APP_BASE_URL || `http://localhost:${PORT}`;
|
const APP_BASE_URL = process.env.APP_BASE_URL || `http://localhost:${PORT}`;
|
||||||
@ -881,6 +881,68 @@ export const apiApplyApprovalEvent = async (data: any) => {
|
|||||||
return (await result.json()).data;
|
return (await result.json()).data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 新增字段
|
||||||
|
export const apiCreateField = async (collectionName: string, data: any) => {
|
||||||
|
const api = await request.newContext({
|
||||||
|
storageState: process.env.PLAYWRIGHT_AUTH_FILE,
|
||||||
|
});
|
||||||
|
const state = await api.storageState();
|
||||||
|
const headers = getHeaders(state);
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"sourceKey": "id",
|
||||||
|
"foreignKey": "orgid",
|
||||||
|
"onDelete": "SET NULL",
|
||||||
|
"name": "dept",
|
||||||
|
"type": "hasMany",
|
||||||
|
"uiSchema": {
|
||||||
|
"x-component": "AssociationField",
|
||||||
|
"x-component-props": {
|
||||||
|
"multiple": true
|
||||||
|
},
|
||||||
|
"title": "dept"
|
||||||
|
},
|
||||||
|
"interface": "o2m",
|
||||||
|
"target": "tt_mnt_dept",
|
||||||
|
"targetKey": "id"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const result = await api.post(`/api/collections/${collectionName}/fields:create`, {
|
||||||
|
headers,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok()) {
|
||||||
|
throw new Error(await result.text());
|
||||||
|
}
|
||||||
|
return (await result.json()).data;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"key": "np4llsa0fsx",
|
||||||
|
"name": "dept1",
|
||||||
|
"type": "hasMany",
|
||||||
|
"interface": "o2m",
|
||||||
|
"collectionName": "tt_mnt_org",
|
||||||
|
"description": null,
|
||||||
|
"parentKey": null,
|
||||||
|
"reverseKey": null,
|
||||||
|
"sourceKey": "id",
|
||||||
|
"foreignKey": "orgid",
|
||||||
|
"onDelete": "SET NULL",
|
||||||
|
"uiSchema": {
|
||||||
|
"x-component": "AssociationField",
|
||||||
|
"x-component-props": {
|
||||||
|
"multiple": true
|
||||||
|
},
|
||||||
|
"title": "dept1"
|
||||||
|
},
|
||||||
|
"target": "tt_mnt_dept",
|
||||||
|
"targetKey": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
const getStorageItem = (key: string, storageState: any) => {
|
const getStorageItem = (key: string, storageState: any) => {
|
||||||
return storageState.origins
|
return storageState.origins
|
||||||
.find((item) => item.origin === APP_BASE_URL)
|
.find((item) => item.origin === APP_BASE_URL)
|
||||||
@ -927,13 +989,15 @@ function getHeaders(storageState: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 用户登录新会话
|
// 用户登录新会话
|
||||||
export const userLogin = async (page: Page, approvalUserEmail: string, approvalUser: string) => {
|
export const userLogin = async (browser: Browser, approvalUserEmail: string, approvalUser: string) => {
|
||||||
await page.goto(`${process.env.APP_BASE_URL}/signin`);
|
const context = await browser.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto('signin');
|
||||||
await page.getByPlaceholder('Email').fill(approvalUserEmail);
|
await page.getByPlaceholder('Email').fill(approvalUserEmail);
|
||||||
await page.getByPlaceholder('Password').fill(approvalUser);
|
await page.getByPlaceholder('Password').fill(approvalUser);
|
||||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
return page;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default module.exports = {
|
export default module.exports = {
|
||||||
@ -956,4 +1020,5 @@ export default module.exports = {
|
|||||||
apiCreateRecordTriggerActionEvent,
|
apiCreateRecordTriggerActionEvent,
|
||||||
apiApplyApprovalEvent,
|
apiApplyApprovalEvent,
|
||||||
userLogin,
|
userLogin,
|
||||||
|
apiCreateField,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user