@cosmicdrift/kumiko-renderer 0.327.0 → 0.328.1
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/dist/app/action-form-shim.d.ts +14 -0
- package/{src/app/action-form-shim.ts → dist/app/action-form-shim.js} +26 -44
- package/dist/app/app-features-context.d.ts +8 -0
- package/dist/app/app-features-context.js +16 -0
- package/dist/app/column-renderers.d.ts +30 -0
- package/dist/app/column-renderers.js +34 -0
- package/dist/app/config-edit-shim.d.ts +11 -0
- package/{src/app/config-edit-shim.ts → dist/app/config-edit-shim.js} +12 -25
- package/dist/app/content-editors.d.ts +38 -0
- package/dist/app/content-editors.js +26 -0
- package/dist/app/content-preview.d.ts +11 -0
- package/dist/app/content-preview.js +30 -0
- package/dist/app/custom-screens.d.ts +11 -0
- package/dist/app/custom-screens.js +20 -0
- package/dist/app/dashboard-body.d.ts +16 -0
- package/dist/app/dashboard-body.js +11 -0
- package/dist/app/extension-form-submit.d.ts +32 -0
- package/dist/app/extension-form-submit.js +73 -0
- package/dist/app/extension-sections.d.ts +72 -0
- package/dist/app/extension-sections.js +34 -0
- package/dist/app/feature-schema.d.ts +4 -0
- package/{src/app/feature-schema.ts → dist/app/feature-schema.js} +12 -22
- package/dist/app/form-schema.d.ts +4 -0
- package/dist/app/form-schema.js +85 -0
- package/dist/app/kumiko-screen.d.ts +43 -0
- package/dist/app/kumiko-screen.js +2206 -0
- package/dist/app/layout-fields.d.ts +3 -0
- package/dist/app/layout-fields.js +20 -0
- package/dist/app/list-facets.d.ts +48 -0
- package/dist/app/list-facets.js +185 -0
- package/dist/app/nav.d.ts +61 -0
- package/dist/app/nav.js +139 -0
- package/dist/app/projection-detail-shim.d.ts +8 -0
- package/dist/app/projection-detail-shim.js +71 -0
- package/dist/app/projection-list-shim.d.ts +13 -0
- package/{src/app/projection-list-shim.ts → dist/app/projection-list-shim.js} +26 -38
- package/dist/app/qn.d.ts +7 -0
- package/{src/app/qn.ts → dist/app/qn.js} +9 -11
- package/dist/app/qualify-screen-id.d.ts +2 -0
- package/{src/app/qualify-screen-id.ts → dist/app/qualify-screen-id.js} +5 -6
- package/dist/app/reference-facet-bridge.d.ts +10 -0
- package/dist/app/reference-facet-bridge.js +22 -0
- package/dist/app/return-to.d.ts +31 -0
- package/dist/app/return-to.js +188 -0
- package/dist/app/row-actions.d.ts +77 -0
- package/dist/app/row-actions.js +416 -0
- package/dist/app/screen-access.d.ts +8 -0
- package/dist/app/screen-access.js +48 -0
- package/dist/app/secret-mint-body.d.ts +10 -0
- package/dist/app/secret-mint-body.js +140 -0
- package/dist/app/secrets-edit-body.d.ts +8 -0
- package/dist/app/secrets-edit-body.js +76 -0
- package/dist/app/use-embedded-screen.d.ts +6 -0
- package/dist/app/use-embedded-screen.js +19 -0
- package/dist/app/variable-chips.d.ts +7 -0
- package/dist/app/variable-chips.js +16 -0
- package/dist/app/write-failed-error.d.ts +6 -0
- package/dist/app/write-failed-error.js +21 -0
- package/dist/components/embedded-list-field.d.ts +10 -0
- package/dist/components/embedded-list-field.js +259 -0
- package/dist/components/grid-cell-for-field.d.ts +25 -0
- package/dist/components/grid-cell-for-field.js +31 -0
- package/dist/components/multi-select-checkboxes.d.ts +14 -0
- package/dist/components/multi-select-checkboxes.js +36 -0
- package/dist/components/reference-create-dialog.d.ts +13 -0
- package/dist/components/reference-create-dialog.js +46 -0
- package/dist/components/related-list-section.d.ts +25 -0
- package/dist/components/related-list-section.js +273 -0
- package/dist/components/render-edit-action-button.d.ts +22 -0
- package/dist/components/render-edit-action-button.js +43 -0
- package/dist/components/render-edit-logic.d.ts +6 -0
- package/dist/components/render-edit-logic.js +63 -0
- package/dist/components/render-edit-types.d.ts +209 -0
- package/dist/components/render-edit-types.js +1 -0
- package/dist/components/render-edit.d.ts +6 -0
- package/dist/components/render-edit.js +1169 -0
- package/dist/components/render-field.d.ts +38 -0
- package/dist/components/render-field.js +544 -0
- package/dist/components/render-list.d.ts +117 -0
- package/dist/components/render-list.js +232 -0
- package/dist/components/write-form-section.d.ts +17 -0
- package/dist/components/write-form-section.js +104 -0
- package/dist/context/dispatcher-context.d.ts +10 -0
- package/{src/context/dispatcher-context.tsx → dist/context/dispatcher-context.js} +16 -29
- package/dist/context/draft-storage-context.d.ts +12 -0
- package/dist/context/draft-storage-context.js +18 -0
- package/dist/context/user-roles-context.d.ts +7 -0
- package/{src/context/user-roles-context.tsx → dist/context/user-roles-context.js} +7 -15
- package/dist/format-when.d.ts +1 -0
- package/{src/format-when.ts → dist/format-when.js} +7 -7
- package/dist/hooks/reference-limits.d.ts +3 -0
- package/{src/hooks/reference-limits.ts → dist/hooks/reference-limits.js} +0 -1
- package/dist/hooks/use-ai-text.d.ts +52 -0
- package/dist/hooks/use-ai-text.js +90 -0
- package/dist/hooks/use-disclosure.d.ts +7 -0
- package/dist/hooks/use-disclosure.js +8 -0
- package/dist/hooks/use-form.d.ts +7 -0
- package/dist/hooks/use-form.js +36 -0
- package/dist/hooks/use-list-url-state.d.ts +43 -0
- package/dist/hooks/use-list-url-state.js +131 -0
- package/dist/hooks/use-mutation.d.ts +9 -0
- package/dist/hooks/use-mutation.js +37 -0
- package/dist/hooks/use-query.d.ts +14 -0
- package/dist/hooks/use-query.js +118 -0
- package/dist/hooks/use-reference-lookup.d.ts +14 -0
- package/dist/hooks/use-reference-lookup.js +63 -0
- package/dist/hooks/use-store.d.ts +3 -0
- package/dist/hooks/use-store.js +38 -0
- package/dist/hooks/use-stream-handler.d.ts +14 -0
- package/dist/hooks/use-stream-handler.js +93 -0
- package/dist/i18n-defaults.d.ts +2 -0
- package/dist/i18n-defaults.js +201 -0
- package/dist/i18n.d.ts +64 -0
- package/dist/i18n.js +166 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +55 -0
- package/dist/inside-drawer.d.ts +2 -0
- package/dist/inside-drawer.js +9 -0
- package/dist/page-header-slot.d.ts +2 -0
- package/dist/page-header-slot.js +9 -0
- package/dist/primitives.d.ts +1351 -0
- package/dist/primitives.js +73 -0
- package/dist/screen-fills-height.d.ts +3 -0
- package/dist/screen-fills-height.js +3 -0
- package/dist/share-links.d.ts +4 -0
- package/dist/share-links.js +11 -0
- package/dist/sort-by-accessor.d.ts +5 -0
- package/dist/sort-by-accessor.js +16 -0
- package/dist/sse/live-events.d.ts +23 -0
- package/dist/sse/live-events.js +28 -0
- package/dist/tokens.d.ts +83 -0
- package/dist/tokens.js +80 -0
- package/package.json +16 -9
- package/src/changes.json +52 -0
- package/src/__tests__/error-i18n-defaults.test.ts +0 -82
- package/src/__tests__/format-when.test.ts +0 -12
- package/src/__tests__/i18n.test.tsx +0 -283
- package/src/__tests__/merge-search-params-into-initial.test.ts +0 -525
- package/src/__tests__/qn.test.ts +0 -83
- package/src/__tests__/share-links.test.ts +0 -26
- package/src/__tests__/sort-by-accessor.test.ts +0 -48
- package/src/__tests__/stringify-nav-params.test.ts +0 -33
- package/src/__tests__/use-list-url-state.test.tsx +0 -250
- package/src/app/__tests__/action-form-field-labels.test.tsx +0 -92
- package/src/app/__tests__/action-form-shim.test.ts +0 -137
- package/src/app/__tests__/build-list-query-payload.test.ts +0 -107
- package/src/app/__tests__/config-edit-shim.test.ts +0 -57
- package/src/app/__tests__/content-editors.test.tsx +0 -128
- package/src/app/__tests__/content-preview.test.tsx +0 -184
- package/src/app/__tests__/entity-edit-create-search-params.test.tsx +0 -236
- package/src/app/__tests__/entity-edit-jsonb-embedded-submit.test.tsx +0 -239
- package/src/app/__tests__/entity-list-create-singleton.test.tsx +0 -128
- package/src/app/__tests__/entity-list-row-action-entity-target.test.tsx +0 -424
- package/src/app/__tests__/entity-list-row-action-icon.test.tsx +0 -248
- package/src/app/__tests__/entity-list-row-action-kumiko-actions-view.test.tsx +0 -162
- package/src/app/__tests__/entity-list-row-action-refetch.test.tsx +0 -401
- package/src/app/__tests__/entity-list-screen-padding.test.tsx +0 -111
- package/src/app/__tests__/feature-schema.test.ts +0 -40
- package/src/app/__tests__/form-schema.test.ts +0 -247
- package/src/app/__tests__/list-daterange-facet.test.ts +0 -165
- package/src/app/__tests__/list-filter-facets.test.tsx +0 -521
- package/src/app/__tests__/nav.test.ts +0 -198
- package/src/app/__tests__/projection-detail-actions.test.tsx +0 -719
- package/src/app/__tests__/projection-detail-reference-field.test.tsx +0 -165
- package/src/app/__tests__/projection-detail-shim.test.ts +0 -188
- package/src/app/__tests__/projection-detail-singleton.test.tsx +0 -191
- package/src/app/__tests__/projection-list-bad-shape.test.tsx +0 -152
- package/src/app/__tests__/projection-list-reference-column.test.tsx +0 -219
- package/src/app/__tests__/projection-list-search-sort.test.tsx +0 -212
- package/src/app/__tests__/qualified-names.test.ts +0 -37
- package/src/app/__tests__/return-to.test.ts +0 -396
- package/src/app/__tests__/row-actions-navigate-tab.test.ts +0 -223
- package/src/app/__tests__/row-default-edit-action.test.tsx +0 -324
- package/src/app/__tests__/screen-access-allows.test.ts +0 -34
- package/src/app/__tests__/secret-mint-body.test.tsx +0 -404
- package/src/app/__tests__/variable-chips.test.tsx +0 -70
- package/src/app/app-features-context.tsx +0 -25
- package/src/app/column-renderers.tsx +0 -68
- package/src/app/content-editors.tsx +0 -85
- package/src/app/content-preview.tsx +0 -67
- package/src/app/custom-screens.tsx +0 -29
- package/src/app/dashboard-body.tsx +0 -33
- package/src/app/extension-form-submit.tsx +0 -123
- package/src/app/extension-sections.tsx +0 -111
- package/src/app/form-schema.ts +0 -91
- package/src/app/kumiko-screen.tsx +0 -3614
- package/src/app/layout-fields.ts +0 -31
- package/src/app/list-facets.ts +0 -259
- package/src/app/nav.tsx +0 -258
- package/src/app/projection-detail-shim.ts +0 -85
- package/src/app/reference-facet-bridge.tsx +0 -58
- package/src/app/return-to.tsx +0 -234
- package/src/app/row-actions.ts +0 -639
- package/src/app/screen-access.ts +0 -63
- package/src/app/secret-mint-body.tsx +0 -259
- package/src/app/secrets-edit-body.tsx +0 -194
- package/src/app/use-embedded-screen.ts +0 -27
- package/src/app/variable-chips.tsx +0 -39
- package/src/app/write-failed-error.ts +0 -27
- package/src/components/__tests__/embedded-list-field.test.tsx +0 -531
- package/src/components/__tests__/multi-select-checkboxes.test.tsx +0 -162
- package/src/components/__tests__/related-list-section-empty-state.test.tsx +0 -143
- package/src/components/__tests__/related-list-section.test.tsx +0 -1669
- package/src/components/__tests__/render-edit-action-button.test.tsx +0 -205
- package/src/components/__tests__/render-edit-hidden-section.test.tsx +0 -128
- package/src/components/__tests__/render-edit-logic.test.ts +0 -314
- package/src/components/__tests__/render-edit-screen-description.test.tsx +0 -152
- package/src/components/__tests__/render-edit-submit-actions.test.tsx +0 -460
- package/src/components/__tests__/render-field-app-locale.test.tsx +0 -142
- package/src/components/__tests__/render-field-enum-option.test.tsx +0 -99
- package/src/components/__tests__/render-field-icon-derivation.test.tsx +0 -119
- package/src/components/__tests__/render-field-json-format.test.tsx +0 -164
- package/src/components/__tests__/render-field-money-embedded-list-consistency.test.tsx +0 -212
- package/src/components/__tests__/render-field-money-roundtrip.test.tsx +0 -217
- package/src/components/__tests__/render-field-number-grouping.test.tsx +0 -76
- package/src/components/__tests__/render-field-number-unit.test.tsx +0 -227
- package/src/components/__tests__/render-field-password-format.test.tsx +0 -101
- package/src/components/__tests__/render-field-select-display.test.tsx +0 -134
- package/src/components/__tests__/render-field-unit-format.test.tsx +0 -107
- package/src/components/__tests__/render-field-unsupported-types.test.tsx +0 -195
- package/src/components/__tests__/render-list-toolbar-icon-only.test.tsx +0 -146
- package/src/components/__tests__/write-form-section.test.tsx +0 -375
- package/src/components/embedded-list-field.tsx +0 -354
- package/src/components/grid-cell-for-field.tsx +0 -60
- package/src/components/multi-select-checkboxes.tsx +0 -93
- package/src/components/reference-create-dialog.tsx +0 -91
- package/src/components/related-list-section.tsx +0 -467
- package/src/components/render-edit-action-button.tsx +0 -106
- package/src/components/render-edit-logic.ts +0 -85
- package/src/components/render-edit-types.ts +0 -216
- package/src/components/render-edit.tsx +0 -1700
- package/src/components/render-field.tsx +0 -942
- package/src/components/render-list.tsx +0 -561
- package/src/components/write-form-section.tsx +0 -199
- package/src/context/draft-storage-context.tsx +0 -39
- package/src/hooks/__tests__/use-ai-text.test.tsx +0 -177
- package/src/hooks/__tests__/use-disclosure.test.tsx +0 -25
- package/src/hooks/__tests__/use-form.test.tsx +0 -65
- package/src/hooks/__tests__/use-mutation.test.tsx +0 -77
- package/src/hooks/__tests__/use-stream-handler.test.tsx +0 -107
- package/src/hooks/use-ai-text.ts +0 -172
- package/src/hooks/use-disclosure.ts +0 -20
- package/src/hooks/use-form.ts +0 -78
- package/src/hooks/use-list-url-state.ts +0 -171
- package/src/hooks/use-mutation.ts +0 -61
- package/src/hooks/use-query.ts +0 -132
- package/src/hooks/use-reference-lookup.ts +0 -76
- package/src/hooks/use-store.ts +0 -48
- package/src/hooks/use-stream-handler.ts +0 -134
- package/src/i18n-defaults.ts +0 -220
- package/src/i18n.tsx +0 -286
- package/src/index.ts +0 -272
- package/src/primitives.tsx +0 -1446
- package/src/share-links.ts +0 -12
- package/src/sort-by-accessor.ts +0 -20
- package/src/sse/live-events.tsx +0 -56
- package/src/tokens.tsx +0 -170
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
// Regression coverage for kumiko-framework#2723: `EntityEditScreenDefinition.
|
|
2
|
-
// description` / `ActionFormScreenDefinition.description` existed in the type
|
|
3
|
-
// but was never rendered anywhere — used only as agent/API metadata
|
|
4
|
-
// (packages/framework/src/api/server.ts). RenderEdit now renders it as the
|
|
5
|
-
// form's subtitle, same visual slot section.description already fills for a
|
|
6
|
-
// section (render-edit.tsx's `Section`), falling back to it only when no
|
|
7
|
-
// `screen:<id>.subtitle` i18n override is set. `description` is run through
|
|
8
|
-
// translate(): an i18n key (the established convention, e.g.
|
|
9
|
-
// user-data-rights/feature.ts) resolves to its translation, plain prose
|
|
10
|
-
// passes through unchanged (review finding on this PR).
|
|
11
|
-
|
|
12
|
-
import { describe, expect, test } from "bun:test";
|
|
13
|
-
import type {
|
|
14
|
-
EntityDefinition,
|
|
15
|
-
EntityEditScreenDefinition,
|
|
16
|
-
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
17
|
-
import { render } from "@testing-library/react";
|
|
18
|
-
import type { ReactNode } from "react";
|
|
19
|
-
import { createStaticLocaleResolver, LocaleProvider, type TranslationsByLocale } from "../../i18n";
|
|
20
|
-
import { kumikoDefaultTranslations } from "../../i18n-defaults";
|
|
21
|
-
import { type CorePrimitives, type FormProps, PrimitivesProvider } from "../../primitives";
|
|
22
|
-
import { RenderEdit } from "../render-edit";
|
|
23
|
-
|
|
24
|
-
const noop = (): ReactNode => null;
|
|
25
|
-
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
26
|
-
|
|
27
|
-
function buildEntity(): EntityDefinition {
|
|
28
|
-
return {
|
|
29
|
-
fields: {
|
|
30
|
-
name: { type: "text", maxLength: 200, required: false, searchable: false, sortable: false },
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function renderEditCapturingForm(
|
|
36
|
-
screen: EntityEditScreenDefinition,
|
|
37
|
-
extraTranslations?: TranslationsByLocale,
|
|
38
|
-
hideSectionTitles = false,
|
|
39
|
-
): {
|
|
40
|
-
captured: FormProps | undefined;
|
|
41
|
-
} {
|
|
42
|
-
let captured: FormProps | undefined;
|
|
43
|
-
const capturingForm = (props: FormProps): ReactNode => {
|
|
44
|
-
captured = props;
|
|
45
|
-
return props.children;
|
|
46
|
-
};
|
|
47
|
-
const primitives: CorePrimitives = {
|
|
48
|
-
Button: noop,
|
|
49
|
-
Banner: noop,
|
|
50
|
-
Field: passChildren,
|
|
51
|
-
Input: noop,
|
|
52
|
-
DataTable: noop,
|
|
53
|
-
Form: capturingForm,
|
|
54
|
-
Section: passChildren,
|
|
55
|
-
Card: passChildren,
|
|
56
|
-
Grid: passChildren,
|
|
57
|
-
GridCell: passChildren,
|
|
58
|
-
Text: passChildren,
|
|
59
|
-
Heading: noop,
|
|
60
|
-
Dialog: noop,
|
|
61
|
-
Modal: noop,
|
|
62
|
-
Lightbox: noop,
|
|
63
|
-
ConfigSourceBadge: noop,
|
|
64
|
-
ConfigCascadeView: noop,
|
|
65
|
-
Link: noop,
|
|
66
|
-
};
|
|
67
|
-
render(
|
|
68
|
-
<LocaleProvider
|
|
69
|
-
resolver={createStaticLocaleResolver({ locale: "en-US" })}
|
|
70
|
-
fallbackBundles={
|
|
71
|
-
extraTranslations !== undefined
|
|
72
|
-
? [extraTranslations, kumikoDefaultTranslations]
|
|
73
|
-
: [kumikoDefaultTranslations]
|
|
74
|
-
}
|
|
75
|
-
>
|
|
76
|
-
<PrimitivesProvider value={primitives}>
|
|
77
|
-
<RenderEdit
|
|
78
|
-
screen={screen}
|
|
79
|
-
entity={buildEntity()}
|
|
80
|
-
featureName="widgets"
|
|
81
|
-
initial={{ name: "" }}
|
|
82
|
-
{...(hideSectionTitles && { hideSectionTitles: true })}
|
|
83
|
-
/>
|
|
84
|
-
</PrimitivesProvider>
|
|
85
|
-
</LocaleProvider>,
|
|
86
|
-
);
|
|
87
|
-
return { captured };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
describe("RenderEdit — screen.description as form subtitle (fw#2723)", () => {
|
|
91
|
-
test("a screen with description renders it as the form's subtitle", () => {
|
|
92
|
-
const screen: EntityEditScreenDefinition = {
|
|
93
|
-
id: "widget-edit",
|
|
94
|
-
type: "entityEdit",
|
|
95
|
-
entity: "widget",
|
|
96
|
-
description: "Edit the widget's basic details.",
|
|
97
|
-
layout: { sections: [{ title: "Basics", fields: ["name"] }] },
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const { captured } = renderEditCapturingForm(screen);
|
|
101
|
-
|
|
102
|
-
expect(captured?.subtitle).toBe("Edit the widget's basic details.");
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("a description that is a known i18n key renders translated, not as the raw key", () => {
|
|
106
|
-
const screen: EntityEditScreenDefinition = {
|
|
107
|
-
id: "widget-edit",
|
|
108
|
-
type: "entityEdit",
|
|
109
|
-
entity: "widget",
|
|
110
|
-
description: "widgets:widget-edit.explainer",
|
|
111
|
-
layout: { sections: [{ title: "Basics", fields: ["name"] }] },
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const { captured } = renderEditCapturingForm(screen, {
|
|
115
|
-
"en-US": { "widgets:widget-edit.explainer": "Every field here is optional." },
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
expect(captured?.subtitle).toBe("Every field here is optional.");
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("a screen without description renders no subtitle prop at all (no empty placeholder)", () => {
|
|
122
|
-
const screen: EntityEditScreenDefinition = {
|
|
123
|
-
id: "widget-edit",
|
|
124
|
-
type: "entityEdit",
|
|
125
|
-
entity: "widget",
|
|
126
|
-
layout: { sections: [{ title: "Basics", fields: ["name"] }] },
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const { captured } = renderEditCapturingForm(screen);
|
|
130
|
-
|
|
131
|
-
expect(captured).toBeDefined();
|
|
132
|
-
expect(captured && "subtitle" in captured).toBe(false);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("tabs mode suppresses a description whose key matches the subtitle probe", () => {
|
|
136
|
-
const screen: EntityEditScreenDefinition = {
|
|
137
|
-
id: "widget-edit",
|
|
138
|
-
type: "entityEdit",
|
|
139
|
-
entity: "widget",
|
|
140
|
-
description: "screen:widget-edit.subtitle",
|
|
141
|
-
layout: { mode: "tabs", sections: [{ title: "Basics", fields: ["name"] }] },
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const { captured } = renderEditCapturingForm(
|
|
145
|
-
screen,
|
|
146
|
-
{ "en-US": { "screen:widget-edit.subtitle": "Widget details." } },
|
|
147
|
-
true,
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
expect(captured && "subtitle" in captured).toBe(false);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type {
|
|
3
|
-
EntityDefinition,
|
|
4
|
-
EntityEditScreenDefinition,
|
|
5
|
-
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
|
-
import type { Dispatcher, DispatcherError, SubmitResult } from "@cosmicdrift/kumiko-headless";
|
|
7
|
-
import { fireEvent, render, screen as rtlScreen, waitFor } from "@testing-library/react";
|
|
8
|
-
import type { ComponentType, ReactNode } from "react";
|
|
9
|
-
import { buildFormSchema } from "../../app/form-schema";
|
|
10
|
-
import { DispatcherProvider } from "../../context/dispatcher-context";
|
|
11
|
-
import { createStaticLocaleResolver, LocaleProvider } from "../../i18n";
|
|
12
|
-
import { kumikoDefaultTranslations } from "../../i18n-defaults";
|
|
13
|
-
import {
|
|
14
|
-
type BannerProps,
|
|
15
|
-
type CorePrimitives,
|
|
16
|
-
PrimitivesProvider,
|
|
17
|
-
type SectionProps,
|
|
18
|
-
type TextProps,
|
|
19
|
-
} from "../../primitives";
|
|
20
|
-
import { RenderEdit, type RenderEditAction, type RenderEditProps } from "../render-edit";
|
|
21
|
-
|
|
22
|
-
type Values = { name: string };
|
|
23
|
-
|
|
24
|
-
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
25
|
-
|
|
26
|
-
const noop = () => {};
|
|
27
|
-
|
|
28
|
-
const renderSection: ComponentType<SectionProps> = ({ testId, children }) => (
|
|
29
|
-
<div data-testid={testId}>{children}</div>
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const testButton: ComponentType<{
|
|
33
|
-
children?: ReactNode;
|
|
34
|
-
onClick?: () => void;
|
|
35
|
-
testId?: string;
|
|
36
|
-
type?: "button" | "submit";
|
|
37
|
-
disabled?: boolean;
|
|
38
|
-
}> = ({ children, onClick, testId, type, disabled }) => (
|
|
39
|
-
<button type={type ?? "button"} data-testid={testId} onClick={onClick} disabled={disabled}>
|
|
40
|
-
{children}
|
|
41
|
-
</button>
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const testForm: ComponentType<{
|
|
45
|
-
children?: ReactNode;
|
|
46
|
-
actions?: ReactNode;
|
|
47
|
-
secondaryActions?: ReactNode;
|
|
48
|
-
onSubmit?: () => void;
|
|
49
|
-
}> = ({ children, actions, secondaryActions, onSubmit }) => (
|
|
50
|
-
<form
|
|
51
|
-
onSubmit={(e) => {
|
|
52
|
-
e.preventDefault();
|
|
53
|
-
onSubmit?.();
|
|
54
|
-
}}
|
|
55
|
-
>
|
|
56
|
-
{children}
|
|
57
|
-
{secondaryActions}
|
|
58
|
-
{actions}
|
|
59
|
-
</form>
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const testInput: ComponentType<{
|
|
63
|
-
name?: string;
|
|
64
|
-
value?: unknown;
|
|
65
|
-
onChange?: (v: unknown) => void;
|
|
66
|
-
}> = ({ name = "field", value, onChange }) => (
|
|
67
|
-
<input
|
|
68
|
-
aria-label={name}
|
|
69
|
-
data-testid={`input-${name}`}
|
|
70
|
-
value={typeof value === "string" ? value : ""}
|
|
71
|
-
onChange={(e) => onChange?.(e.target.value)}
|
|
72
|
-
/>
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const testBanner: ComponentType<BannerProps> = ({ children, testId }) => (
|
|
76
|
-
<div data-testid={testId}>{children}</div>
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const testText: ComponentType<TextProps> = ({ children, testId }) => (
|
|
80
|
-
<span data-testid={testId}>{children}</span>
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
function testPrimitives(): CorePrimitives {
|
|
84
|
-
return {
|
|
85
|
-
Button: testButton,
|
|
86
|
-
Banner: testBanner,
|
|
87
|
-
Field: passChildren,
|
|
88
|
-
Input: testInput,
|
|
89
|
-
DataTable: noop,
|
|
90
|
-
Form: testForm,
|
|
91
|
-
Section: renderSection,
|
|
92
|
-
Card: passChildren,
|
|
93
|
-
Grid: passChildren,
|
|
94
|
-
GridCell: passChildren,
|
|
95
|
-
Text: testText,
|
|
96
|
-
Heading: noop,
|
|
97
|
-
Dialog: noop,
|
|
98
|
-
Modal: noop,
|
|
99
|
-
Lightbox: noop,
|
|
100
|
-
ConfigSourceBadge: noop,
|
|
101
|
-
ConfigCascadeView: noop,
|
|
102
|
-
Link: noop,
|
|
103
|
-
} as unknown as CorePrimitives;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function buildEntity(required = false): EntityDefinition {
|
|
107
|
-
return {
|
|
108
|
-
fields: {
|
|
109
|
-
name: {
|
|
110
|
-
type: "text",
|
|
111
|
-
maxLength: 200,
|
|
112
|
-
required,
|
|
113
|
-
searchable: false,
|
|
114
|
-
sortable: false,
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function stubDispatcher(writeImpl?: Dispatcher["write"]): {
|
|
121
|
-
dispatcher: Dispatcher;
|
|
122
|
-
writes: Array<{ type: string; payload: unknown }>;
|
|
123
|
-
} {
|
|
124
|
-
const writes: Array<{ type: string; payload: unknown }> = [];
|
|
125
|
-
const dispatcher: Dispatcher = {
|
|
126
|
-
write: (async (type, payload) => {
|
|
127
|
-
writes.push({ type, payload });
|
|
128
|
-
if (writeImpl) return writeImpl(type, payload);
|
|
129
|
-
return { isSuccess: true, data: { id: "n1" } };
|
|
130
|
-
}) as Dispatcher["write"],
|
|
131
|
-
query: (async () => ({ isSuccess: true, data: {} })) as Dispatcher["query"],
|
|
132
|
-
batch: (async () => ({ isSuccess: true, results: [] })) as Dispatcher["batch"],
|
|
133
|
-
statusStore: {
|
|
134
|
-
getState: () => "online",
|
|
135
|
-
subscribe: () => () => {},
|
|
136
|
-
} as unknown as Dispatcher["statusStore"],
|
|
137
|
-
async *stream() {},
|
|
138
|
-
pendingWrites: () => [],
|
|
139
|
-
pendingFiles: () => [],
|
|
140
|
-
};
|
|
141
|
-
return { dispatcher, writes };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function renderEdit(
|
|
145
|
-
screen: EntityEditScreenDefinition,
|
|
146
|
-
overrides: Partial<RenderEditProps<Values>> = {},
|
|
147
|
-
entity: EntityDefinition = buildEntity(),
|
|
148
|
-
dispatcher?: Dispatcher,
|
|
149
|
-
) {
|
|
150
|
-
const body = (
|
|
151
|
-
<LocaleProvider
|
|
152
|
-
resolver={createStaticLocaleResolver({ locale: "en-US" })}
|
|
153
|
-
fallbackBundles={[kumikoDefaultTranslations]}
|
|
154
|
-
>
|
|
155
|
-
<PrimitivesProvider value={testPrimitives()}>
|
|
156
|
-
<RenderEdit
|
|
157
|
-
screen={screen}
|
|
158
|
-
entity={entity}
|
|
159
|
-
featureName="contacts"
|
|
160
|
-
initial={{ name: "" }}
|
|
161
|
-
{...overrides}
|
|
162
|
-
/>
|
|
163
|
-
</PrimitivesProvider>
|
|
164
|
-
</LocaleProvider>
|
|
165
|
-
);
|
|
166
|
-
return render(
|
|
167
|
-
dispatcher !== undefined ? (
|
|
168
|
-
<DispatcherProvider dispatcher={dispatcher}>{body}</DispatcherProvider>
|
|
169
|
-
) : (
|
|
170
|
-
body
|
|
171
|
-
),
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const oneFieldScreen: EntityEditScreenDefinition = {
|
|
176
|
-
id: "contact-edit",
|
|
177
|
-
type: "entityEdit",
|
|
178
|
-
entity: "contact",
|
|
179
|
-
layout: { sections: [{ title: "Main", fields: ["name"] }] },
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const writeFailure: DispatcherError = {
|
|
183
|
-
code: "conflict",
|
|
184
|
-
httpStatus: 409,
|
|
185
|
-
i18nKey: "errors.conflict",
|
|
186
|
-
message: "conflict",
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
describe("RenderEdit — submit path", () => {
|
|
190
|
-
test("changing a field and saving calls onSubmit with a successful result", async () => {
|
|
191
|
-
let submitted: SubmitResult<unknown> | undefined;
|
|
192
|
-
renderEdit(oneFieldScreen, {
|
|
193
|
-
customSubmit: async () => ({
|
|
194
|
-
validationBlocked: false,
|
|
195
|
-
isSuccess: true,
|
|
196
|
-
data: { id: "n1" },
|
|
197
|
-
}),
|
|
198
|
-
onSubmit: (result) => {
|
|
199
|
-
submitted = result;
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "Ferdinand" } });
|
|
204
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
205
|
-
|
|
206
|
-
await waitFor(() => expect(submitted).toBeDefined());
|
|
207
|
-
expect(submitted?.isSuccess).toBe(true);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test("submit stays disabled while the form is unchanged and enables after edits", async () => {
|
|
211
|
-
const view = renderEdit(oneFieldScreen, {
|
|
212
|
-
customSubmit: async () => ({ validationBlocked: false, isSuccess: true, data: {} }),
|
|
213
|
-
onSubmit: () => {
|
|
214
|
-
throw new Error("must not fire while disabled");
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const save = rtlScreen.getByTestId("render-edit-submit") as HTMLButtonElement;
|
|
219
|
-
expect(save.disabled).toBe(true);
|
|
220
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "X" } });
|
|
221
|
-
await waitFor(() => expect(save.disabled).toBe(false));
|
|
222
|
-
view.unmount();
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("failed customSubmit surfaces the form-error banner and notifies onSubmit", async () => {
|
|
226
|
-
let submitted: SubmitResult<unknown> | undefined;
|
|
227
|
-
let customCalls = 0;
|
|
228
|
-
renderEdit(oneFieldScreen, {
|
|
229
|
-
customSubmit: async () => {
|
|
230
|
-
customCalls += 1;
|
|
231
|
-
return { validationBlocked: false, isSuccess: false, error: writeFailure };
|
|
232
|
-
},
|
|
233
|
-
onSubmit: (result) => {
|
|
234
|
-
submitted = result;
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "Ferdinand" } });
|
|
239
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
240
|
-
|
|
241
|
-
await waitFor(() => expect(rtlScreen.getByTestId("render-edit-form-error")).toBeTruthy());
|
|
242
|
-
// LocaleProvider resolves i18nKey → human copy; pin the error code via onSubmit.
|
|
243
|
-
expect(rtlScreen.getByTestId("render-edit-form-error-key").textContent?.length).toBeGreaterThan(
|
|
244
|
-
0,
|
|
245
|
-
);
|
|
246
|
-
expect(customCalls).toBe(1);
|
|
247
|
-
expect(submitted).toEqual({
|
|
248
|
-
validationBlocked: false,
|
|
249
|
-
isSuccess: false,
|
|
250
|
-
error: writeFailure,
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("schema validation blocks customSubmit and notifies validationBlocked", async () => {
|
|
255
|
-
let submitted: SubmitResult<unknown> | undefined;
|
|
256
|
-
let customCalls = 0;
|
|
257
|
-
const entity = buildEntity(true);
|
|
258
|
-
// RenderEdit only validates when a schema is passed (kumiko-screen builds it
|
|
259
|
-
// via buildFormSchema). Without it, entity.required is display-only here.
|
|
260
|
-
render(
|
|
261
|
-
<LocaleProvider
|
|
262
|
-
resolver={createStaticLocaleResolver({ locale: "en-US" })}
|
|
263
|
-
fallbackBundles={[kumikoDefaultTranslations]}
|
|
264
|
-
>
|
|
265
|
-
<PrimitivesProvider value={testPrimitives()}>
|
|
266
|
-
<RenderEdit
|
|
267
|
-
screen={oneFieldScreen}
|
|
268
|
-
entity={entity}
|
|
269
|
-
featureName="contacts"
|
|
270
|
-
initial={{ name: "seed" }}
|
|
271
|
-
schema={buildFormSchema(entity, oneFieldScreen)}
|
|
272
|
-
customSubmit={async () => {
|
|
273
|
-
customCalls += 1;
|
|
274
|
-
return { validationBlocked: false, isSuccess: true, data: {} };
|
|
275
|
-
}}
|
|
276
|
-
onSubmit={(result) => {
|
|
277
|
-
submitted = result;
|
|
278
|
-
}}
|
|
279
|
-
/>
|
|
280
|
-
</PrimitivesProvider>
|
|
281
|
-
</LocaleProvider>,
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
// Clear the required field; form stays dirty vs initial "seed".
|
|
285
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "" } });
|
|
286
|
-
await waitFor(() =>
|
|
287
|
-
expect((rtlScreen.getByTestId("render-edit-submit") as HTMLButtonElement).disabled).toBe(
|
|
288
|
-
false,
|
|
289
|
-
),
|
|
290
|
-
);
|
|
291
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
292
|
-
|
|
293
|
-
await waitFor(() => expect(submitted).toBeDefined());
|
|
294
|
-
expect(submitted).toEqual({ validationBlocked: true, isSuccess: false });
|
|
295
|
-
expect(customCalls).toBe(0);
|
|
296
|
-
});
|
|
297
|
-
test("failed writeCommand with field issues suppresses the form-error banner", async () => {
|
|
298
|
-
const writeFailure: DispatcherError = {
|
|
299
|
-
code: "validation_failed",
|
|
300
|
-
httpStatus: 422,
|
|
301
|
-
i18nKey: "kumiko.errors.validation",
|
|
302
|
-
message: "validation failed",
|
|
303
|
-
details: {
|
|
304
|
-
fields: [{ path: "name", code: "too_small", i18nKey: "kumiko.errors.required" }],
|
|
305
|
-
},
|
|
306
|
-
};
|
|
307
|
-
const { dispatcher } = stubDispatcher(async () => ({
|
|
308
|
-
isSuccess: false,
|
|
309
|
-
error: writeFailure,
|
|
310
|
-
}));
|
|
311
|
-
renderEdit(
|
|
312
|
-
oneFieldScreen,
|
|
313
|
-
{ writeCommand: "contacts:write:contact:update" },
|
|
314
|
-
buildEntity(),
|
|
315
|
-
dispatcher,
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "Ada" } });
|
|
319
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
320
|
-
|
|
321
|
-
await waitFor(() => expect(rtlScreen.queryByTestId("render-edit-form-error")).toBeNull());
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// fw#2838: an input-less secretMint mint step declares an empty entity
|
|
325
|
-
// (fields: {}) and an empty layout (sections: []) — such a form can never
|
|
326
|
-
// go dirty, so the unchanged-gate must not permanently disable its submit.
|
|
327
|
-
test("a fieldless form's submit button is not disabled and stays visible", () => {
|
|
328
|
-
const zeroFieldScreen: EntityEditScreenDefinition = {
|
|
329
|
-
id: "trigger",
|
|
330
|
-
type: "entityEdit",
|
|
331
|
-
entity: "trigger",
|
|
332
|
-
layout: { sections: [] },
|
|
333
|
-
};
|
|
334
|
-
renderEdit(zeroFieldScreen, {}, { fields: {} });
|
|
335
|
-
|
|
336
|
-
const save = rtlScreen.getByTestId("render-edit-submit") as HTMLButtonElement;
|
|
337
|
-
expect(save.disabled).toBe(false);
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
describe("RenderEdit — custom actions", () => {
|
|
341
|
-
test("renders an action button and runs its handler on click", async () => {
|
|
342
|
-
let pressed = 0;
|
|
343
|
-
const actions: readonly RenderEditAction[] = [
|
|
344
|
-
{
|
|
345
|
-
id: "ping",
|
|
346
|
-
label: "Ping",
|
|
347
|
-
onPress: async () => {
|
|
348
|
-
pressed += 1;
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
];
|
|
352
|
-
renderEdit(oneFieldScreen, { actions });
|
|
353
|
-
|
|
354
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-action-ping"));
|
|
355
|
-
await waitFor(() => expect(pressed).toBe(1));
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
test("action handler failure shows the action-error banner", async () => {
|
|
359
|
-
const actions: readonly RenderEditAction[] = [
|
|
360
|
-
{
|
|
361
|
-
id: "boom",
|
|
362
|
-
label: "Boom",
|
|
363
|
-
onPress: async () => {
|
|
364
|
-
throw new Error("action exploded");
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
];
|
|
368
|
-
renderEdit(oneFieldScreen, { actions });
|
|
369
|
-
|
|
370
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-action-boom"));
|
|
371
|
-
await waitFor(() => expect(rtlScreen.getByTestId("render-edit-action-error")).toBeTruthy());
|
|
372
|
-
expect(rtlScreen.getByTestId("render-edit-action-error").textContent).toContain(
|
|
373
|
-
"action exploded",
|
|
374
|
-
);
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
describe("RenderEdit — writeCommand path", () => {
|
|
379
|
-
test("successful writeCommand dispatches and notifies onSubmit", async () => {
|
|
380
|
-
const { dispatcher, writes } = stubDispatcher();
|
|
381
|
-
let submitted: SubmitResult<unknown> | undefined;
|
|
382
|
-
renderEdit(
|
|
383
|
-
oneFieldScreen,
|
|
384
|
-
{
|
|
385
|
-
writeCommand: "contacts:write:contact:update",
|
|
386
|
-
onSubmit: (result) => {
|
|
387
|
-
submitted = result;
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
buildEntity(),
|
|
391
|
-
dispatcher,
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "Ada" } });
|
|
395
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
396
|
-
|
|
397
|
-
await waitFor(() => expect(submitted).toBeDefined());
|
|
398
|
-
expect(submitted?.isSuccess).toBe(true);
|
|
399
|
-
expect(writes).toHaveLength(1);
|
|
400
|
-
expect(writes[0]?.type).toBe("contacts:write:contact:update");
|
|
401
|
-
expect(writes[0]?.payload).toMatchObject({ name: "Ada" });
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
test("failed writeCommand without field issues shows form-error banner", async () => {
|
|
405
|
-
const writeFailure: DispatcherError = {
|
|
406
|
-
code: "conflict",
|
|
407
|
-
httpStatus: 409,
|
|
408
|
-
i18nKey: "errors.conflict",
|
|
409
|
-
message: "conflict",
|
|
410
|
-
};
|
|
411
|
-
const { dispatcher } = stubDispatcher(async () => ({
|
|
412
|
-
isSuccess: false,
|
|
413
|
-
error: writeFailure,
|
|
414
|
-
}));
|
|
415
|
-
let submitted: SubmitResult<unknown> | undefined;
|
|
416
|
-
renderEdit(
|
|
417
|
-
oneFieldScreen,
|
|
418
|
-
{
|
|
419
|
-
writeCommand: "contacts:write:contact:update",
|
|
420
|
-
onSubmit: (result) => {
|
|
421
|
-
submitted = result;
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
buildEntity(),
|
|
425
|
-
dispatcher,
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "Ada" } });
|
|
429
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
430
|
-
|
|
431
|
-
await waitFor(() => expect(rtlScreen.getByTestId("render-edit-form-error")).toBeTruthy());
|
|
432
|
-
expect(submitted?.isSuccess).toBe(false);
|
|
433
|
-
});
|
|
434
|
-
test("failed writeCommand with field issues suppresses the form-error banner", async () => {
|
|
435
|
-
const writeFailure: DispatcherError = {
|
|
436
|
-
code: "validation_failed",
|
|
437
|
-
httpStatus: 422,
|
|
438
|
-
i18nKey: "kumiko.errors.validation",
|
|
439
|
-
message: "validation failed",
|
|
440
|
-
details: {
|
|
441
|
-
fields: [{ path: "name", code: "too_small", i18nKey: "kumiko.errors.required" }],
|
|
442
|
-
},
|
|
443
|
-
};
|
|
444
|
-
const { dispatcher } = stubDispatcher(async () => ({
|
|
445
|
-
isSuccess: false,
|
|
446
|
-
error: writeFailure,
|
|
447
|
-
}));
|
|
448
|
-
renderEdit(
|
|
449
|
-
oneFieldScreen,
|
|
450
|
-
{ writeCommand: "contacts:write:contact:update" },
|
|
451
|
-
buildEntity(),
|
|
452
|
-
dispatcher,
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
fireEvent.change(rtlScreen.getByLabelText(/name/i), { target: { value: "Ada" } });
|
|
456
|
-
fireEvent.click(rtlScreen.getByTestId("render-edit-submit"));
|
|
457
|
-
|
|
458
|
-
await waitFor(() => expect(rtlScreen.queryByTestId("render-edit-form-error")).toBeNull());
|
|
459
|
-
});
|
|
460
|
-
});
|