diff --git a/packages/amis-core/src/utils/helper.ts b/packages/amis-core/src/utils/helper.ts index 1fc82f09c..577ee6487 100644 --- a/packages/amis-core/src/utils/helper.ts +++ b/packages/amis-core/src/utils/helper.ts @@ -2325,4 +2325,12 @@ export class TestIdBuilder { [TEST_ID_KEY]: data ? filter(this.testId, data) : this.testId }; } + + getTestIdValue(data?: object) { + if (this.testId == null) { + return undefined; + } + + return data ? filter(this.testId, data) : this.testId; + } } diff --git a/packages/amis-ui/src/components/Tabs.tsx b/packages/amis-ui/src/components/Tabs.tsx index 6a049253c..9ee12f944 100644 --- a/packages/amis-ui/src/components/Tabs.tsx +++ b/packages/amis-ui/src/components/Tabs.tsx @@ -5,7 +5,13 @@ */ import React from 'react'; -import {ClassName, localeable, LocaleProps, Schema} from 'amis-core'; +import { + ClassName, + localeable, + LocaleProps, + Schema, + TestIdBuilder +} from 'amis-core'; import Transition, {ENTERED, ENTERING} from 'react-transition-group/Transition'; import {themeable, ThemeProps, noop} from 'amis-core'; import {uncontrollable} from 'amis-core'; @@ -59,6 +65,7 @@ export interface TabProps extends ThemeProps { children?: React.ReactNode | Array; swipeable?: boolean; onSelect?: (eventKey: string | number) => void; + testIdBuilder?: TestIdBuilder; } class TabComponent extends React.PureComponent { @@ -113,7 +120,8 @@ class TabComponent extends React.PureComponent { children, className, swipeable, - mobileUI + mobileUI, + testIdBuilder } = this.props; return ( @@ -140,6 +148,7 @@ class TabComponent extends React.PureComponent { onTouchMove={swipeable && mobileUI ? this.onTouchMove : noop} onTouchEnd={swipeable && mobileUI ? this.onTouchEnd : noop} onTouchCancel={swipeable && mobileUI ? this.onTouchEnd : noop} + {...testIdBuilder?.getTestId()} > {children} @@ -181,6 +190,7 @@ export interface TabsProps extends ThemeProps, LocaleProps { collapseBtnLabel?: string; popOverContainer?: any; children?: React.ReactNode | Array; + testIdBuilder?: TestIdBuilder; } export interface IDragInfo { @@ -586,7 +596,8 @@ export class Tabs extends React.Component { draggable, showTip, showTipClassName, - editable + editable, + testIdBuilder } = this.props; const { @@ -646,7 +657,9 @@ export class Tabs extends React.Component { )} ); - + const tabTestIdBuidr = testIdBuilder?.getChild( + `tab-${typeof title === 'string' ? title : index}` + ); return (
  • { typeof title === 'string' && this.handleStartEdit(index, title); }} + {...tabTestIdBuidr?.getChild('link').getTestId()} > {showTip ? ( { this.props.onClose && this.props.onClose(index, eventKey ?? index); }} + {...tabTestIdBuidr?.getChild('close').getTestId()} > @@ -722,7 +737,7 @@ export class Tabs extends React.Component { } renderArrow(type: 'left' | 'right') { - const {mode: dMode, tabsMode} = this.props; + const {mode: dMode, tabsMode, testIdBuilder} = this.props; const mode = tabsMode || dMode; if (['vertical', 'sidebar'].includes(mode)) { return; @@ -738,6 +753,7 @@ export class Tabs extends React.Component { 'Tabs-linksContainer-arrow--' + type, disabled && 'Tabs-linksContainer-arrow--disabled' )} + {...testIdBuilder?.getChild(`arrow-${type}`).getTestId()} > @@ -835,7 +851,8 @@ export class Tabs extends React.Component { draggable, sidePosition, addBtnText, - mobileUI + mobileUI, + testIdBuilder } = this.props; const {isOverflow} = this.state; @@ -851,6 +868,7 @@ export class Tabs extends React.Component {
    this.handleAddBtn()} + {...testIdBuilder?.getChild('add-tab').getTestId()} > {addBtnText} @@ -871,6 +889,7 @@ export class Tabs extends React.Component { className )} style={style} + {...testIdBuilder?.getTestId()} > {!['vertical', 'sidebar', 'chrome'].includes(mode) ? (
    { 'Tabs-linksContainer', isOverflow && 'Tabs-linksContainer--overflow' )} + {...testIdBuilder?.getChild('links').getTestId()} > {!mobileUI ? this.renderArrow('left') : null}
    @@ -911,6 +931,7 @@ export class Tabs extends React.Component { 'is-mobile': mobileUI })} role="tablist" + {...testIdBuilder?.getChild('links').getTestId()} > {this.renderNavs()} {additionBtns} @@ -925,7 +946,11 @@ export class Tabs extends React.Component { })}
    {draggable && ( -
    +
    )}
    ); diff --git a/packages/amis/src/renderers/Table/Cell.tsx b/packages/amis/src/renderers/Table/Cell.tsx index eaf07c435..bac3a088a 100644 --- a/packages/amis/src/renderers/Table/Cell.tsx +++ b/packages/amis/src/renderers/Table/Cell.tsx @@ -7,7 +7,8 @@ import { ThemeProps, resolveVariable, buildTrackExpression, - evalTrackExpression + evalTrackExpression, + TestIdBuilder } from 'amis-core'; import {BadgeObject, Checkbox, Icon, Spinner} from 'amis-ui'; import React from 'react'; @@ -33,6 +34,7 @@ export interface CellProps extends ThemeProps { quickEditFormRef: any; onImageEnlarge?: any; translate: (key: string, ...args: Array) => string; + testIdBuilder: TestIdBuilder; } export default function Cell({ @@ -53,7 +55,8 @@ export default function Cell({ popOverContainer, quickEditFormRef, onImageEnlarge, - translate: __ + translate: __, + testIdBuilder }: CellProps) { if (column.name && item.rowSpans[column.name] === 0) { return null; @@ -77,6 +80,7 @@ export default function Cell({ ); @@ -95,6 +100,7 @@ export default function Cell({ className={cx(column.pristine.className, stickyClassName, { 'is-dragDisabled': !item.draggable })} + {...testIdBuilder.getChild('drag').getTestId()} > {item.draggable ? : null} @@ -111,6 +117,9 @@ export default function Cell({ // data-tooltip="展开/收起" // data-position="top" onClick={item.toggleExpanded} + {...testIdBuilder + .getChild(item.expanded ? 'fold' : 'expand') + .getTestId()} > @@ -142,6 +151,7 @@ export default function Cell({ key="retryBtn" onClick={item.resetDefered} data-tooltip={__('Options.retry', {reason: item.error})} + {...testIdBuilder.getChild('retry').getTestId()} > @@ -152,6 +162,9 @@ export default function Cell({ // data-tooltip="展开/收起" // data-position="top" onClick={item.toggleExpanded} + {...testIdBuilder + .getChild(item.expanded ? 'fold' : 'expand') + .getTestId()} > @@ -174,6 +187,7 @@ export default function Cell({ draggable onDragStart={onDragStart} className={cx('Table-dragBtn')} + {...testIdBuilder.getChild('drag').getTestId()} > @@ -245,7 +259,8 @@ export default function Cell({ { ...column.pristine, column: column.pristine, - type: 'cell' + type: 'cell', + testid: testIdBuilder.getTestIdValue() }, subProps ); diff --git a/packages/amis/src/renderers/Table/TableBody.tsx b/packages/amis/src/renderers/Table/TableBody.tsx index f9d6fed8c..9528af0d4 100644 --- a/packages/amis/src/renderers/Table/TableBody.tsx +++ b/packages/amis/src/renderers/Table/TableBody.tsx @@ -72,7 +72,8 @@ export class TableBody extends React.Component { renderRows( rows: Array, columns = this.props.columns, - rowProps: any = {} + rowProps: any = {}, + indexPath?: string ): any { const { rowClassName, @@ -99,16 +100,20 @@ export class TableBody extends React.Component { return rows.map((item: IRow, rowIndex: number) => { const itemProps = buildItemProps ? buildItemProps(item, rowIndex) : null; + const rowPath = `${indexPath ? indexPath + '/' : ''}${rowIndex}`; + const rowTestBuidr = testIdBuilder?.getChild(`row-${rowPath}`); + const doms = [ { checkOnItemClick={checkOnItemClick} key={`foot-${item.id}`} itemIndex={rowIndex} + rowPath={rowPath} item={item} itemClassName={cx( rowClassNameExpr @@ -167,16 +173,22 @@ export class TableBody extends React.Component { onQuickChange={onQuickChange} ignoreFootableContent={ignoreFootableContent} {...rowProps} + testIdBuilder={rowTestBuidr} /> ); } } else if (item.children.length && item.expanded) { // 嵌套表格 doms.push( - ...this.renderRows(item.children, columns, { - ...rowProps, - parent: item - }) + ...this.renderRows( + item.children, + columns, + { + ...rowProps, + parent: item + }, + rowPath + ) ); } return doms; diff --git a/packages/amis/src/renderers/Table/TableRow.tsx b/packages/amis/src/renderers/Table/TableRow.tsx index 9a9e38db4..62ab01edf 100644 --- a/packages/amis/src/renderers/Table/TableRow.tsx +++ b/packages/amis/src/renderers/Table/TableRow.tsx @@ -45,6 +45,7 @@ interface TableRowProps extends Pick { checkOnItemClick?: boolean; ignoreFootableContent?: boolean; testIdBuilder?: TestIdBuilder; + rowPath: string; // 整体行的路径,树形时需要父行序号/当前展开层级下的行序号 [propName: string]: any; } @@ -205,6 +206,7 @@ export class TableRow extends React.PureComponent< trRef, isNested, testIdBuilder, + rowPath, ...rest } = this.props; @@ -267,6 +269,7 @@ export class TableRow extends React.PureComponent< width: null, rowIndex: itemIndex, colIndex: column.index, + rowPath, key: column.index, onAction: this.handleAction, onQuickChange: this.handleQuickChange, @@ -328,11 +331,11 @@ export class TableRow extends React.PureComponent< ...rest, rowIndex: itemIndex, colIndex: column.index, + rowPath, key: column.id, onAction: this.handleAction, onQuickChange: this.handleQuickChange, - onChange: this.handleChange, - testIdBuilder: testIdBuilder?.getChild(`col${column.index}`) + onChange: this.handleChange }) ) : column.name && item.rowSpans[column.name] === 0 ? null : ( diff --git a/packages/amis/src/renderers/Table/index.tsx b/packages/amis/src/renderers/Table/index.tsx index 947e3a39c..cb6cfb591 100644 --- a/packages/amis/src/renderers/Table/index.tsx +++ b/packages/amis/src/renderers/Table/index.tsx @@ -2076,7 +2076,8 @@ export default class Table extends React.Component { classnames: cx, canAccessSuperData, itemBadge, - translate + translate, + testIdBuilder } = this.props; return ( @@ -2100,6 +2101,9 @@ export default class Table extends React.Component { quickEditFormRef={this.subFormRef} onImageEnlarge={this.handleImageEnlarge} translate={translate} + testIdBuilder={testIdBuilder.getChild( + `cell-${props.rowPath}-${column.index}` + )} /> ); } diff --git a/packages/amis/src/renderers/Tabs.tsx b/packages/amis/src/renderers/Tabs.tsx index cc84b2b1c..48b64a245 100644 --- a/packages/amis/src/renderers/Tabs.tsx +++ b/packages/amis/src/renderers/Tabs.tsx @@ -763,7 +763,8 @@ export default class Tabs extends React.Component { collapseBtnLabel, disabled, mobileUI, - swipeable + swipeable, + testIdBuilder } = this.props; const mode = tabsMode || dMode; @@ -809,6 +810,9 @@ export default class Tabs extends React.Component { : unmountOnExit } onSelect={this.handleSelect} + testIdBuilder={testIdBuilder.getChild( + `tab-${typeof tab.title === 'string' ? tab.title : index}` + )} > {render( `item/${index}`, @@ -850,6 +854,9 @@ export default class Tabs extends React.Component { : unmountOnExit } onSelect={this.handleSelect} + testIdBuilder={testIdBuilder.getChild( + `tab-${typeof tab.title === 'string' ? tab.title : index}` + )} > {this.renderTab ? this.renderTab(tab, this.props, index) @@ -897,6 +904,7 @@ export default class Tabs extends React.Component { collapseOnExceed={collapseOnExceed} collapseBtnLabel={collapseBtnLabel} mobileUI={mobileUI} + testIdBuilder={testIdBuilder} > {children}