fix(Tabs): prevent mismatched activeKey (#5412)
Some checks are pending
Auto merge main -> next / push-commit (push) Waiting to run
Build docker image / build-and-push (push) Waiting to run
Build pro image / build-and-push (push) Waiting to run
Deploy client docs / Build (push) Waiting to run
E2E / Build (push) Waiting to run
E2E / Core and plugins (push) Blocked by required conditions
E2E / plugin-workflow (push) Blocked by required conditions
E2E / plugin-workflow-approval (push) Blocked by required conditions
E2E / plugin-data-source-main (push) Blocked by required conditions
E2E / Comment on PR (push) Blocked by required conditions
NocoBase frontEnd test / frontend-test (18) (push) Waiting to run

* test: add e2e test

* fix(Tabs): prevent mismatched activeKey

* fix(bulkEdit): prevent incorrect URL modification

* Revert "fix(bulkEdit): prevent incorrect URL modification"

This reverts commit 28f685ffeb.

* fix: avoid affecting the Tabs of the upper-level popup

* fix(Duplicate): avoid affecting the Tabs of the upper-level popup

* refactor: migrate e2e to plugin

* test(Duplicate): add e2e test
This commit is contained in:
Zeke Zhang 2024-10-16 09:25:07 +08:00 committed by GitHub
parent 01f7c39e9b
commit c2a8dced55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 1811 additions and 12 deletions

View File

@ -15,7 +15,7 @@ import _, { default as lodash } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next';
import { ErrorFallback, StablePopover, useActionContext } from '../..';
import { ErrorFallback, StablePopover, TabsContextProvider, useActionContext } from '../..';
import { useDesignable } from '../../';
import { useACLActionParamsContext } from '../../../acl';
import { useCollectionParentRecordData, useCollectionRecordData, useDataBlockRequest } from '../../../data-source';
@ -181,7 +181,7 @@ export const Action: ComposedAction = withDynamicSchemaProps(
// return buttonElement;
// }
const result = (
let result = (
<PopupVisibleProvider visible={false}>
<ActionContextProvider
button={buttonElement}
@ -206,6 +206,11 @@ export const Action: ComposedAction = withDynamicSchemaProps(
</PopupVisibleProvider>
);
if (isBulkEditAction(fieldSchema)) {
// Clear the context of Tabs to avoid affecting the Tabs of the upper-level popup
result = <TabsContextProvider>{result}</TabsContextProvider>;
}
// fix https://nocobase.height.app/T-3235/description
if (addChild) {
return wrapSSR(

View File

@ -0,0 +1,32 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { expect, test } from '@nocobase/test/e2e';
import { theAddBlockButtonInDrawerShouldBeVisible } from './utils';
test.describe('popup of bulk edit', () => {
test('the Add block button in drawer should be visible', async ({ page, mockPage }) => {
await mockPage(theAddBlockButtonInDrawerShouldBeVisible).goto();
// open subpage, anb then open the bulk edit drawer, the Add block in drawer should be visible
await page.getByLabel('action-Action.Link-open').click();
await page.getByLabel('action-Action-Bulk edit 1-').click();
await expect(
page.getByTestId('drawer-Action.Container-users-Bulk edit').getByLabel('schema-initializer-Grid-popup'),
).toBeVisible();
await page.getByLabel('drawer-Action.Container-users-Bulk edit-mask').click();
// click other tab, then open the bulk edit drawer, the Add block in drawer should be visible
await page.getByText('new tab').click();
await page.getByLabel('action-Action-Bulk edit 2-').click();
await expect(
page.getByTestId('drawer-Action.Container-users-Bulk edit').getByLabel('schema-initializer-Grid-popup'),
).toBeVisible();
});
});

View File

@ -541,3 +541,705 @@ export const oneEmptyTableBlockWithCustomizeActions: PageConfig = {
'x-async': true,
},
};
export const theAddBlockButtonInDrawerShouldBeVisible = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
'x-app-version': '1.3.32-beta',
properties: {
ydtgms2lmr4: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
'x-app-version': '1.3.32-beta',
properties: {
jk8ix7ivmvb: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.3.32-beta',
properties: {
fdohqzh304u: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.3.32-beta',
properties: {
syp961jrbvs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'users:list',
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: 'users',
dataSource: 'main',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-app-version': '1.3.32-beta',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-app-version': '1.3.32-beta',
'x-uid': 'd9ciqysn98f',
'x-async': false,
'x-index': 1,
},
bflk6vcqbda: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-use-component-props': 'useTableBlockProps',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
},
'x-app-version': '1.3.32-beta',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-toolbar': 'TableColumnSchemaToolbar',
'x-initializer': 'table:configureItemActions',
'x-settings': 'fieldSettings:TableColumn',
'x-toolbar-props': {
initializer: 'table:configureItemActions',
},
'x-app-version': '1.3.32-beta',
properties: {
'9fy397o5n7x': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '1.3.32-beta',
properties: {
ujxoicj1do2: {
'x-uid': 'stie1dytwk9',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'open subpage',
'x-action': 'view',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:view',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'page',
iconColor: '#1677FF',
danger: false,
},
'x-action-context': {
dataSource: 'main',
collection: 'users',
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'popup:common:addBlock',
properties: {
gkbkej9bcfg: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.3.32-beta',
properties: {
qsnxw1kyglw: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.3.32-beta',
properties: {
rg5p0jxa04f: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'users:list',
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: 'users',
dataSource: 'main',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-app-version': '1.3.32-beta',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-app-version': '1.3.32-beta',
properties: {
yrh6fyeotth: {
'x-uid': 'm9eykj7hcac',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'Bulk edit 1',
'x-component': 'Action',
'x-action': 'customize:bulkEdit',
'x-action-settings': {
updateMode: 'selected',
},
'x-component-props': {
openMode: 'drawer',
icon: 'EditOutlined',
iconColor: '#1677FF',
danger: false,
type: 'default',
},
'x-align': 'right',
'x-decorator': 'BulkEditActionDecorator',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:bulkEdit',
'x-acl-action': 'update',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-app-version': '1.3.32-beta',
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Bulk edit")}}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
'x-app-version': '1.3.32-beta',
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
'x-initializer-props': {
gridInitializer:
'popup:bulkEdit:addBlock',
},
'x-app-version': '1.3.32-beta',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Bulk edit")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-app-version': '1.3.32-beta',
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer':
'popup:bulkEdit:addBlock',
'x-app-version': '1.3.32-beta',
'x-uid': '86kvljipurj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'osjgnzq0bxu',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'nddbbs6e5vc',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'r604iwattq7',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'kdyyiruax36',
'x-async': false,
'x-index': 1,
},
lodqxwvvrwu: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-use-component-props': 'useTableBlockProps',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
},
'x-app-version': '1.3.32-beta',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-toolbar': 'TableColumnSchemaToolbar',
'x-initializer': 'table:configureItemActions',
'x-settings': 'fieldSettings:TableColumn',
'x-toolbar-props': {
initializer: 'table:configureItemActions',
},
'x-app-version': '1.3.32-beta',
properties: {
'1gt1zh3wlhl': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '1.3.32-beta',
'x-uid': 'r3rllrh7wzk',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'dmx697o309f',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'labwg68zvn6',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '0fn0tjzur2c',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'goinlb9fan9',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '74qg393u7hr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'hetu8763puj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '4dn6kb0cmwp',
'x-async': false,
'x-index': 1,
},
gljvwj6dtl8: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'new tab',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-app-version': '1.3.32-beta',
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'popup:common:addBlock',
'x-app-version': '1.3.32-beta',
properties: {
c1yhos175hj: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.3.32-beta',
properties: {
yhbbftd4j86: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.3.32-beta',
properties: {
'4y4cdnzx645': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'users:list',
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: 'users',
dataSource: 'main',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-app-version': '1.3.32-beta',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-app-version': '1.3.32-beta',
properties: {
vp75269u5ai: {
'x-uid': '3dup1gvthik',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'Bulk edit 2',
'x-component': 'Action',
'x-action': 'customize:bulkEdit',
'x-action-settings': {
updateMode: 'selected',
},
'x-component-props': {
openMode: 'drawer',
icon: 'EditOutlined',
iconColor: '#1677FF',
danger: false,
type: 'default',
},
'x-align': 'right',
'x-decorator': 'BulkEditActionDecorator',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:bulkEdit',
'x-acl-action': 'update',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-app-version': '1.3.32-beta',
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Bulk edit")}}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
'x-app-version': '1.3.32-beta',
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
'x-initializer-props': {
gridInitializer:
'popup:bulkEdit:addBlock',
},
'x-app-version': '1.3.32-beta',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Bulk edit")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-app-version': '1.3.32-beta',
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer':
'popup:bulkEdit:addBlock',
'x-app-version': '1.3.32-beta',
'x-uid': 'ev7zy0yictp',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'n6f6k6fvukf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xm9xscjd3am',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'tpukizpyz29',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'mf55d2cpdpx',
'x-async': false,
'x-index': 1,
},
p5i670h31zl: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-use-component-props': 'useTableBlockProps',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
},
'x-app-version': '1.3.32-beta',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-toolbar': 'TableColumnSchemaToolbar',
'x-initializer': 'table:configureItemActions',
'x-settings': 'fieldSettings:TableColumn',
'x-toolbar-props': {
initializer: 'table:configureItemActions',
},
'x-app-version': '1.3.32-beta',
properties: {
owyte0l5cgt: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '1.3.32-beta',
'x-uid': 'bvc3y7y3fem',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '4xso516rjk9',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'dyyuo828wdo',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'um86bfrnd0n',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'bwkvo3771d1',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '8ecnnc4yvgz',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '6pmwqi2etjn',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'e88jnt5tv4e',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '2u9lxm7qivt',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'kqabk7m6d7h',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'eqyqzt5letc',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7t4bewrinmr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ouboqexioby',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'e363l9dbg2g',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '9p4tvagqpfd',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'a68euip3d3s',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'hklfkdijm58',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '536m19repzv',
'x-async': true,
'x-index': 1,
},
};

View File

@ -15,6 +15,7 @@ import {
FormBlockContext,
PopupSettingsProvider,
RecordProvider,
TabsContextProvider,
fetchTemplateData,
useACLActionParamsContext,
useAPIClient,
@ -200,16 +201,19 @@ export const DuplicateAction = observer(
{loading ? t('Duplicating') : children || t('Duplicate')}
</Button>
)}
<CollectionProvider_deprecated name={duplicateCollection || name}>
{/* 这里的 record 就是弹窗中创建表单的 sourceRecord */}
<RecordProvider record={{ ...parentRecordData, __collection: duplicateCollection || __collection }}>
<ActionContextProvider value={{ ...ctx, visible, setVisible }}>
<PopupSettingsProvider enableURL={false}>
<RecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties />
</PopupSettingsProvider>
</ActionContextProvider>
</RecordProvider>
</CollectionProvider_deprecated>
{/* Clear the context of Tabs to avoid affecting the Tabs of the upper-level popup */}
<TabsContextProvider>
<CollectionProvider_deprecated name={duplicateCollection || name}>
{/* 这里的 record 就是弹窗中创建表单的 sourceRecord */}
<RecordProvider record={{ ...parentRecordData, __collection: duplicateCollection || __collection }}>
<ActionContextProvider value={{ ...ctx, visible, setVisible }}>
<PopupSettingsProvider enableURL={false}>
<RecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties />
</PopupSettingsProvider>
</ActionContextProvider>
</RecordProvider>
</CollectionProvider_deprecated>
</TabsContextProvider>
</div>
</FormBlockContext.Provider>
</div>

View File

@ -0,0 +1,28 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { expect, test } from '@nocobase/test/e2e';
import { theAddBlockButtonInDrawerShouldBeVisible } from './templates';
test.describe('popup of duplicate', () => {
test('the Add block button in drawer should be visible', async ({ page, mockPage }) => {
await mockPage(theAddBlockButtonInDrawerShouldBeVisible).goto();
// open subpage, anb then open the duplicate drawer, the Add block in drawer should be visible
await page.getByLabel('action-Action.Link-open').click();
await page.getByLabel('action-Action.Link-Duplicate').click();
await expect(page.getByLabel('block-item-Markdown.Void-').getByText('Duplicate markdown 1.')).toBeVisible();
await page.getByLabel('drawer-Action.Container-users-Duplicate-mask').click();
// click other tab, then open the bulk edit drawer, the Add block in drawer should be visible
await page.getByText('new tab').click();
await page.getByLabel('action-Action.Link-Duplicate').click();
await expect(page.getByLabel('block-item-Markdown.Void-').getByText('Duplicate markdown 2.')).toBeVisible();
});
});