@nocobase/client-v2 2.1.0-alpha.34 → 2.1.0-alpha.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/BaseApplication.d.ts +1 -0
- package/es/PluginManager.d.ts +1 -0
- package/es/components/form/DrawerFormLayout.d.ts +49 -0
- package/es/components/form/EnvVariableInput.d.ts +42 -0
- package/es/components/form/FileSizeInput.d.ts +27 -0
- package/es/components/form/createFormRegistry.d.ts +33 -0
- package/es/components/form/index.d.ts +13 -0
- package/es/components/index.d.ts +1 -1
- package/es/flow/components/FieldAssignRulesEditor.d.ts +1 -0
- package/es/flow/internal/utils/enumOptionsUtils.d.ts +5 -0
- package/es/flow/models/actions/AssociationActionUtils.d.ts +5 -0
- package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +4 -0
- package/es/flow/models/blocks/filter-manager/FilterManager.d.ts +5 -1
- package/es/flow/models/blocks/shared/legacyDefaultValueMigrationBase.d.ts +1 -0
- package/es/flow/models/blocks/table/TableSelectModel.d.ts +8 -0
- package/es/flow/models/fields/DisplayEnumFieldModel.d.ts +6 -0
- package/es/flow/models/fields/DisplayTitleFieldModel.d.ts +1 -1
- package/es/flow/models/utils/displayValueUtils.d.ts +12 -0
- package/es/flow-compat/passwordUtils.d.ts +1 -1
- package/es/index.mjs +117 -106
- package/es/utils/remotePlugins.d.ts +0 -4
- package/lib/index.js +122 -111
- package/package.json +8 -5
- package/src/BaseApplication.tsx +14 -8
- package/src/PluginManager.ts +1 -0
- package/src/__tests__/app.test.tsx +28 -1
- package/src/__tests__/remotePlugins.test.ts +29 -18
- package/src/components/form/DrawerFormLayout.tsx +103 -0
- package/src/components/form/EnvVariableInput.tsx +126 -0
- package/src/components/form/FileSizeInput.tsx +105 -0
- package/src/components/form/createFormRegistry.ts +60 -0
- package/src/components/form/index.tsx +14 -0
- package/src/components/index.ts +1 -1
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +92 -13
- package/src/flow/actions/__tests__/formAssignRules.legacyMigration.test.tsx +173 -0
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +476 -1
- package/src/flow/actions/filterFormDefaultValues.tsx +30 -9
- package/src/flow/actions/formAssignRules.tsx +24 -9
- package/src/flow/actions/linkageRules.tsx +240 -258
- package/src/flow/actions/setTargetDataScope.tsx +32 -3
- package/src/flow/actions/titleField.tsx +1 -1
- package/src/flow/actions/validation.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +0 -4
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +13 -5
- package/src/flow/components/FieldAssignRulesEditor.tsx +2 -0
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +81 -4
- package/src/flow/components/filter/LinkageFilterItem.tsx +9 -2
- package/src/flow/components/filter/VariableFilterItem.tsx +2 -6
- package/src/flow/components/filter/__tests__/LinkageFilterItem.test.tsx +71 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +48 -0
- package/src/flow/internal/utils/__tests__/enumOptionsUtils.test.ts +10 -1
- package/src/flow/internal/utils/enumOptionsUtils.ts +29 -0
- package/src/flow/models/actions/AssociateActionModel.tsx +2 -2
- package/src/flow/models/actions/AssociationActionUtils.ts +14 -0
- package/src/flow/models/actions/__tests__/AssociationActionModel.test.ts +63 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +7 -0
- package/src/flow/models/base/PageModel/RootPageModel.tsx +1 -1
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +33 -9
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +53 -13
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +63 -3
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +33 -1
- package/src/flow/models/blocks/filter-form/__tests__/legacyDefaultValueMigration.test.ts +3 -0
- package/src/flow/models/blocks/filter-manager/FilterManager.ts +66 -2
- package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +270 -0
- package/src/flow/models/blocks/form/FormBlockModel.tsx +8 -5
- package/src/flow/models/blocks/form/FormItemModel.tsx +1 -1
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +30 -0
- package/src/flow/models/blocks/form/__tests__/legacyDefaultValueMigration.test.ts +3 -0
- package/src/flow/models/blocks/form/value-runtime/rules.ts +6 -1
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +6 -1
- package/src/flow/models/blocks/shared/legacyDefaultValueMigrationBase.ts +21 -5
- package/src/flow/models/blocks/table/TableBlockModel.tsx +11 -6
- package/src/flow/models/blocks/table/TableColumnModel.tsx +8 -2
- package/src/flow/models/blocks/table/TableSelectModel.tsx +36 -26
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowClick.test.ts +69 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +132 -1
- package/src/flow/models/blocks/table/__tests__/TableSelectModel.test.ts +41 -0
- package/src/flow/models/fields/ClickableFieldModel.tsx +9 -4
- package/src/flow/models/fields/DisplayEnumFieldModel.tsx +44 -0
- package/src/flow/models/fields/DisplayTitleFieldModel.tsx +12 -4
- package/src/flow/models/fields/SelectFieldModel.tsx +31 -1
- package/src/flow/models/fields/__tests__/ClickableFieldModel.test.ts +23 -0
- package/src/flow/models/fields/__tests__/DisplayEnumFieldModel.test.tsx +39 -0
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +2 -1
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +7 -0
- package/src/flow/models/utils/displayValueUtils.ts +57 -0
- package/src/utils/globalDeps.ts +8 -0
- package/src/utils/remotePlugins.ts +7 -27
|
@@ -8,10 +8,31 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { describe, expect, it, vi } from 'vitest';
|
|
11
|
+
import { FlowEngine, MultiRecordResource } from '@nocobase/flow-engine';
|
|
11
12
|
import { dataScope } from '../dataScope';
|
|
12
13
|
import { normalizeDataScopeFilter } from '../dataScopeFilter';
|
|
13
14
|
import { setTargetDataScope } from '../setTargetDataScope';
|
|
14
15
|
|
|
16
|
+
function createSetTargetDataScopeContext(resource: any, options: { selected?: boolean; resolvedValue?: any } = {}) {
|
|
17
|
+
const targetModel = { resource };
|
|
18
|
+
const inputArgs = options.selected === undefined ? undefined : { selected: options.selected };
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
...(inputArgs ? { inputArgs } : {}),
|
|
22
|
+
model: {
|
|
23
|
+
uid: 'action-1',
|
|
24
|
+
scheduleModelOperation: vi.fn((_uid, callback) => callback(targetModel)),
|
|
25
|
+
},
|
|
26
|
+
resolveJsonTemplate: vi.fn(async (template) => ({
|
|
27
|
+
...template,
|
|
28
|
+
filter: {
|
|
29
|
+
...template.filter,
|
|
30
|
+
items: [{ ...template.filter.items[0], value: options.resolvedValue }],
|
|
31
|
+
},
|
|
32
|
+
})),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
describe('normalizeDataScopeFilter', () => {
|
|
16
37
|
it('keeps null when a right-side variable resolves to empty', () => {
|
|
17
38
|
const rawFilter = {
|
|
@@ -125,34 +146,92 @@ describe('normalizeDataScopeFilter', () => {
|
|
|
125
146
|
hasData: vi.fn(() => false),
|
|
126
147
|
refresh: vi.fn(),
|
|
127
148
|
};
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
149
|
+
const ctx = createSetTargetDataScopeContext(resource);
|
|
150
|
+
const params = {
|
|
151
|
+
targetBlockUid: 'target-1',
|
|
152
|
+
filter: {
|
|
153
|
+
logic: '$and',
|
|
154
|
+
items: [{ path: 'departmentId', operator: '$eq', value: '{{ ctx.formValues.department.id }}' }],
|
|
133
155
|
},
|
|
134
|
-
resolveJsonTemplate: vi.fn(async (template) => ({
|
|
135
|
-
...template,
|
|
136
|
-
filter: {
|
|
137
|
-
...template.filter,
|
|
138
|
-
items: [{ ...template.filter.items[0], value: undefined }],
|
|
139
|
-
},
|
|
140
|
-
})),
|
|
141
156
|
};
|
|
157
|
+
|
|
158
|
+
await (setTargetDataScope as any).handler(ctx, params);
|
|
159
|
+
|
|
160
|
+
expect(ctx.model.scheduleModelOperation).toHaveBeenCalledWith('target-1', expect.any(Function));
|
|
161
|
+
expect(resource.addFilterGroup).toHaveBeenCalledWith('setTargetDataScope_action-1', {
|
|
162
|
+
$and: [{ departmentId: { $eq: null } }],
|
|
163
|
+
});
|
|
164
|
+
expect(resource.removeFilterGroup).not.toHaveBeenCalled();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('setTargetDataScope handler removes clicked-row data scope when row click deselects', async () => {
|
|
168
|
+
const resource = {
|
|
169
|
+
addFilterGroup: vi.fn(),
|
|
170
|
+
removeFilterGroup: vi.fn(),
|
|
171
|
+
hasData: vi.fn(() => true),
|
|
172
|
+
refresh: vi.fn(),
|
|
173
|
+
};
|
|
174
|
+
const ctx = createSetTargetDataScopeContext(resource, { selected: false, resolvedValue: null });
|
|
142
175
|
const params = {
|
|
143
176
|
targetBlockUid: 'target-1',
|
|
144
177
|
filter: {
|
|
145
178
|
logic: '$and',
|
|
146
|
-
items: [{ path: '
|
|
179
|
+
items: [{ path: 'id', operator: '$eq', value: '{{ ctx.clickedRowRecord.id }}' }],
|
|
147
180
|
},
|
|
148
181
|
};
|
|
149
182
|
|
|
150
183
|
await (setTargetDataScope as any).handler(ctx, params);
|
|
151
184
|
|
|
152
185
|
expect(ctx.model.scheduleModelOperation).toHaveBeenCalledWith('target-1', expect.any(Function));
|
|
186
|
+
expect(resource.removeFilterGroup).toHaveBeenCalledWith('setTargetDataScope_action-1');
|
|
187
|
+
expect(resource.addFilterGroup).not.toHaveBeenCalled();
|
|
188
|
+
expect(resource.refresh).toHaveBeenCalledTimes(1);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('setTargetDataScope handler keeps null clicked-row field values while row is selected', async () => {
|
|
192
|
+
const resource = {
|
|
193
|
+
addFilterGroup: vi.fn(),
|
|
194
|
+
removeFilterGroup: vi.fn(),
|
|
195
|
+
hasData: vi.fn(() => false),
|
|
196
|
+
refresh: vi.fn(),
|
|
197
|
+
};
|
|
198
|
+
const ctx = createSetTargetDataScopeContext(resource, { selected: true, resolvedValue: null });
|
|
199
|
+
const params = {
|
|
200
|
+
targetBlockUid: 'target-1',
|
|
201
|
+
filter: {
|
|
202
|
+
logic: '$and',
|
|
203
|
+
items: [{ path: 'departmentId', operator: '$eq', value: '{{ ctx.clickedRowRecord.departmentId }}' }],
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
await (setTargetDataScope as any).handler(ctx, params);
|
|
208
|
+
|
|
153
209
|
expect(resource.addFilterGroup).toHaveBeenCalledWith('setTargetDataScope_action-1', {
|
|
154
210
|
$and: [{ departmentId: { $eq: null } }],
|
|
155
211
|
});
|
|
156
212
|
expect(resource.removeFilterGroup).not.toHaveBeenCalled();
|
|
157
213
|
});
|
|
214
|
+
|
|
215
|
+
it('setTargetDataScope handler refreshes empty loaded target while preserving its own data scope', async () => {
|
|
216
|
+
const engine = new FlowEngine();
|
|
217
|
+
const resource = engine.createResource(MultiRecordResource);
|
|
218
|
+
resource.addFilterGroup('target-table', { status: { $eq: 'active' } });
|
|
219
|
+
resource.addFilterGroup('setTargetDataScope_action-1', { id: { $eq: 1 } });
|
|
220
|
+
resource.setData([]);
|
|
221
|
+
resource.setMeta({ count: 0, hasNext: false });
|
|
222
|
+
const refresh = vi.spyOn(resource, 'refresh').mockResolvedValue(undefined);
|
|
223
|
+
const ctx = createSetTargetDataScopeContext(resource, { selected: false, resolvedValue: null });
|
|
224
|
+
const params = {
|
|
225
|
+
targetBlockUid: 'target-1',
|
|
226
|
+
filter: {
|
|
227
|
+
logic: '$and',
|
|
228
|
+
items: [{ path: 'id', operator: '$eq', value: '{{ ctx.clickedRowRecord.id }}' }],
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
await (setTargetDataScope as any).handler(ctx, params);
|
|
233
|
+
|
|
234
|
+
expect(resource.getRequestParameter('filter')).toBe(JSON.stringify({ $and: [{ status: { $eq: 'active' } }] }));
|
|
235
|
+
expect(refresh).toHaveBeenCalledTimes(1);
|
|
236
|
+
});
|
|
158
237
|
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { render, waitFor } from '@testing-library/react';
|
|
12
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
|
+
import { FlowEngine, FlowModel, FlowSettingsContextProvider } from '@nocobase/flow-engine';
|
|
14
|
+
import { formAssignRules } from '../formAssignRules';
|
|
15
|
+
import { filterFormDefaultValues } from '../filterFormDefaultValues';
|
|
16
|
+
|
|
17
|
+
const mockState = vi.hoisted(() => ({
|
|
18
|
+
editorProps: [] as any[],
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock('../../components/FieldAssignRulesEditor', () => ({
|
|
22
|
+
FieldAssignRulesEditor: (props: any) => {
|
|
23
|
+
mockState.editorProps.push(props);
|
|
24
|
+
return null;
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock('../../components/fieldAssignOptions', () => ({
|
|
29
|
+
collectFieldAssignCascaderOptions: vi.fn(() => []),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock('../../components/useAssociationTitleFieldSync', () => ({
|
|
33
|
+
useAssociationTitleFieldSync: () => ({
|
|
34
|
+
isTitleFieldCandidate: vi.fn(() => false),
|
|
35
|
+
onSyncAssociationTitleField: vi.fn(),
|
|
36
|
+
}),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
vi.mock('../../internal/utils/modelUtils', () => ({
|
|
40
|
+
findFormItemModelByFieldPath: vi.fn(() => null),
|
|
41
|
+
getCollectionFromModel: vi.fn(() => null),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
function createLegacyField(fieldPath: string, value: any, legacyFlowKey: string) {
|
|
45
|
+
return {
|
|
46
|
+
props: {},
|
|
47
|
+
stepParams: {
|
|
48
|
+
fieldSettings: { init: { fieldPath } },
|
|
49
|
+
[legacyFlowKey]: { initialValue: { defaultValue: value } },
|
|
50
|
+
},
|
|
51
|
+
getProps() {
|
|
52
|
+
return this.props;
|
|
53
|
+
},
|
|
54
|
+
getStepParams(flowKey: string, stepKey: string) {
|
|
55
|
+
return this.stepParams?.[flowKey]?.[stepKey];
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createModel(legacyFlowKey: string, fields = [{ fieldPath: 'description', value: 'Legacy description' }]) {
|
|
61
|
+
const engine = new FlowEngine();
|
|
62
|
+
engine.translate = vi.fn((key: string) => key) as any;
|
|
63
|
+
const model = new FlowModel({ uid: `model-${legacyFlowKey}`, flowEngine: engine }) as any;
|
|
64
|
+
model.subModels.grid = {
|
|
65
|
+
subModels: {
|
|
66
|
+
items: fields.map((field) => createLegacyField(field.fieldPath, field.value, legacyFlowKey)),
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
return model;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getActionComponent(action: any) {
|
|
73
|
+
const schema = typeof action.uiSchema === 'function' ? action.uiSchema() : action.uiSchema;
|
|
74
|
+
return schema.value['x-component'];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function renderAction(action: any, model: any, initialValue: any[] = []) {
|
|
78
|
+
const Comp = getActionComponent(action);
|
|
79
|
+
const onChange = vi.fn();
|
|
80
|
+
|
|
81
|
+
const Harness = () => {
|
|
82
|
+
const [value, setValue] = React.useState(initialValue);
|
|
83
|
+
const handleChange = React.useCallback(
|
|
84
|
+
(next: any[]) => {
|
|
85
|
+
onChange(next);
|
|
86
|
+
setValue(next);
|
|
87
|
+
},
|
|
88
|
+
[onChange],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<FlowSettingsContextProvider value={model.context}>
|
|
93
|
+
<Comp value={value} onChange={handleChange} />
|
|
94
|
+
</FlowSettingsContextProvider>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
render(<Harness />);
|
|
99
|
+
return { onChange };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function lastEditorValue() {
|
|
103
|
+
return mockState.editorProps.at(-1)?.value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
describe('Field values legacy default migration', () => {
|
|
107
|
+
beforeEach(() => {
|
|
108
|
+
mockState.editorProps.length = 0;
|
|
109
|
+
vi.clearAllMocks();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('imports form field legacy defaults before form-level value is persisted', async () => {
|
|
113
|
+
const model = createModel('editItemSettings');
|
|
114
|
+
const { onChange } = renderAction(formAssignRules, model);
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(onChange).toHaveBeenCalledWith([
|
|
118
|
+
expect.objectContaining({
|
|
119
|
+
key: 'legacy-default:description',
|
|
120
|
+
targetPath: 'description',
|
|
121
|
+
mode: 'default',
|
|
122
|
+
value: 'Legacy description',
|
|
123
|
+
}),
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
expect(lastEditorValue()).toEqual([
|
|
127
|
+
expect.objectContaining({
|
|
128
|
+
targetPath: 'description',
|
|
129
|
+
value: 'Legacy description',
|
|
130
|
+
}),
|
|
131
|
+
]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('does not re-import form legacy defaults after an empty form-level value is persisted', async () => {
|
|
135
|
+
const model = createModel('editItemSettings');
|
|
136
|
+
model.setStepParams('formModelSettings', 'assignRules', { value: [] });
|
|
137
|
+
const { onChange } = renderAction(formAssignRules, model, []);
|
|
138
|
+
|
|
139
|
+
await waitFor(() => {
|
|
140
|
+
expect(mockState.editorProps.length).toBeGreaterThan(1);
|
|
141
|
+
});
|
|
142
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
143
|
+
expect(lastEditorValue()).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('does not append deleted legacy form targets when another rule is already persisted', async () => {
|
|
147
|
+
const persisted = [{ key: 'kept', targetPath: 'title', mode: 'default', value: 'Kept title' }];
|
|
148
|
+
const model = createModel('editItemSettings', [
|
|
149
|
+
{ fieldPath: 'title', value: 'Kept title' },
|
|
150
|
+
{ fieldPath: 'description', value: 'Legacy description' },
|
|
151
|
+
]);
|
|
152
|
+
model.setStepParams('formModelSettings', 'assignRules', { value: persisted });
|
|
153
|
+
const { onChange } = renderAction(formAssignRules, model, persisted);
|
|
154
|
+
|
|
155
|
+
await waitFor(() => {
|
|
156
|
+
expect(mockState.editorProps.length).toBeGreaterThan(1);
|
|
157
|
+
});
|
|
158
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
159
|
+
expect(lastEditorValue()).toEqual(persisted);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('does not re-import filter form legacy defaults after an empty form-level value is persisted', async () => {
|
|
163
|
+
const model = createModel('filterFormItemSettings');
|
|
164
|
+
model.setStepParams('formFilterBlockModelSettings', 'defaultValues', { value: [] });
|
|
165
|
+
const { onChange } = renderAction(filterFormDefaultValues, model, []);
|
|
166
|
+
|
|
167
|
+
await waitFor(() => {
|
|
168
|
+
expect(mockState.editorProps.length).toBeGreaterThan(1);
|
|
169
|
+
});
|
|
170
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
171
|
+
expect(lastEditorValue()).toEqual([]);
|
|
172
|
+
});
|
|
173
|
+
});
|