@cratis/scene.components 4.2.0 → 4.2.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/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -16
- package/bindings/for_bindingRegistry/when_a_command_is_registered.ts +0 -30
- package/bindings/for_bindingRegistry/when_a_query_is_not_registered.ts +0 -17
- package/bindings/for_bindingRegistry/when_a_query_is_registered.ts +0 -30
- package/bindings/for_bindingRegistry/when_bindings_are_cleared.ts +0 -29
- package/bindings/for_bindingRegistry/when_registering_query_identities.ts +0 -81
- package/bindings/for_requireArcProxy/NativeProxies.ts +0 -28
- package/bindings/for_requireArcProxy/when_mounting_bound_adapters.tsx +0 -73
- package/bindings/for_requireArcProxy/when_previewing_without_arc.tsx +0 -37
- package/bindings/for_requireArcProxy/when_validating_a_proxy.ts +0 -44
- package/bindings/for_resolveElementBinding/when_resolving_a_command_binding.ts +0 -34
- package/bindings/for_resolveElementBinding/when_resolving_a_query_binding.ts +0 -45
- package/data/for_SceneDataTable/when_forwarding_query_arguments.tsx +0 -123
- package/data/for_SceneDataTable/when_the_query_binding_is_missing.tsx +0 -48
- package/data/for_SceneSingleResult/when_executing_a_native_query.tsx +0 -290
- package/data/for_SceneSingleResult/when_gating_the_lazy_runtime.tsx +0 -60
- package/dist/esm/bindings/for_bindingRegistry/when_a_command_is_registered.d.ts +0 -2
- package/dist/esm/bindings/for_bindingRegistry/when_a_command_is_registered.d.ts.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_a_command_is_registered.js +0 -25
- package/dist/esm/bindings/for_bindingRegistry/when_a_command_is_registered.js.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_not_registered.d.ts +0 -2
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_not_registered.d.ts.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_not_registered.js +0 -12
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_not_registered.js.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_registered.d.ts +0 -2
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_registered.d.ts.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_registered.js +0 -23
- package/dist/esm/bindings/for_bindingRegistry/when_a_query_is_registered.js.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_bindings_are_cleared.d.ts +0 -2
- package/dist/esm/bindings/for_bindingRegistry/when_bindings_are_cleared.d.ts.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_bindings_are_cleared.js +0 -18
- package/dist/esm/bindings/for_bindingRegistry/when_bindings_are_cleared.js.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.d.ts +0 -2
- package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.d.ts.map +0 -1
- package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.js +0 -73
- package/dist/esm/bindings/for_bindingRegistry/when_registering_query_identities.js.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/NativeProxies.d.ts +0 -25
- package/dist/esm/bindings/for_requireArcProxy/NativeProxies.d.ts.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/NativeProxies.js +0 -23
- package/dist/esm/bindings/for_requireArcProxy/NativeProxies.js.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.d.ts +0 -2
- package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.d.ts.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.js +0 -56
- package/dist/esm/bindings/for_requireArcProxy/when_mounting_bound_adapters.js.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.d.ts +0 -2
- package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.d.ts.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.js +0 -32
- package/dist/esm/bindings/for_requireArcProxy/when_previewing_without_arc.js.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.d.ts +0 -2
- package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.d.ts.map +0 -1
- package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.js +0 -45
- package/dist/esm/bindings/for_requireArcProxy/when_validating_a_proxy.js.map +0 -1
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_command_binding.d.ts +0 -2
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_command_binding.d.ts.map +0 -1
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_command_binding.js +0 -25
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_command_binding.js.map +0 -1
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_query_binding.d.ts +0 -2
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_query_binding.d.ts.map +0 -1
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_query_binding.js +0 -33
- package/dist/esm/bindings/for_resolveElementBinding/when_resolving_a_query_binding.js.map +0 -1
- package/dist/esm/data/for_SceneDataTable/when_forwarding_query_arguments.d.ts +0 -2
- package/dist/esm/data/for_SceneDataTable/when_forwarding_query_arguments.d.ts.map +0 -1
- package/dist/esm/data/for_SceneDataTable/when_forwarding_query_arguments.js +0 -107
- package/dist/esm/data/for_SceneDataTable/when_forwarding_query_arguments.js.map +0 -1
- package/dist/esm/data/for_SceneDataTable/when_the_query_binding_is_missing.d.ts +0 -2
- package/dist/esm/data/for_SceneDataTable/when_the_query_binding_is_missing.d.ts.map +0 -1
- package/dist/esm/data/for_SceneDataTable/when_the_query_binding_is_missing.js +0 -36
- package/dist/esm/data/for_SceneDataTable/when_the_query_binding_is_missing.js.map +0 -1
- package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.d.ts +0 -2
- package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.d.ts.map +0 -1
- package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.js +0 -255
- package/dist/esm/data/for_SceneSingleResult/when_executing_a_native_query.js.map +0 -1
- package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.d.ts +0 -2
- package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.d.ts.map +0 -1
- package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.js +0 -54
- package/dist/esm/data/for_SceneSingleResult/when_gating_the_lazy_runtime.js.map +0 -1
- package/dist/esm/editors/for_SceneObjectNavigationalBar/when_navigating_back_up_the_trail.d.ts +0 -2
- package/dist/esm/editors/for_SceneObjectNavigationalBar/when_navigating_back_up_the_trail.d.ts.map +0 -1
- package/dist/esm/editors/for_SceneObjectNavigationalBar/when_navigating_back_up_the_trail.js +0 -15
- package/dist/esm/editors/for_SceneObjectNavigationalBar/when_navigating_back_up_the_trail.js.map +0 -1
- package/dist/esm/editors/for_filterDefinitions/when_reading_filter_definitions.d.ts +0 -2
- package/dist/esm/editors/for_filterDefinitions/when_reading_filter_definitions.d.ts.map +0 -1
- package/dist/esm/editors/for_filterDefinitions/when_reading_filter_definitions.js +0 -39
- package/dist/esm/editors/for_filterDefinitions/when_reading_filter_definitions.js.map +0 -1
- package/dist/esm/editors/for_timeMachineVersions/when_reading_versions.d.ts +0 -2
- package/dist/esm/editors/for_timeMachineVersions/when_reading_versions.d.ts.map +0 -1
- package/dist/esm/editors/for_timeMachineVersions/when_reading_versions.js +0 -32
- package/dist/esm/editors/for_timeMachineVersions/when_reading_versions.js.map +0 -1
- package/dist/esm/for_cratisComponents/when_rendering_a_screen_through_the_real_renderer.d.ts +0 -2
- package/dist/esm/for_cratisComponents/when_rendering_a_screen_through_the_real_renderer.d.ts.map +0 -1
- package/dist/esm/for_cratisComponents/when_rendering_a_screen_through_the_real_renderer.js +0 -25
- package/dist/esm/for_cratisComponents/when_rendering_a_screen_through_the_real_renderer.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_a_profile_ranks_it_above_the_packages_it_layers_on.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_a_profile_ranks_it_above_the_packages_it_layers_on.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_a_profile_ranks_it_above_the_packages_it_layers_on.js +0 -34
- package/dist/esm/for_cratisComponentsPackage/when_a_profile_ranks_it_above_the_packages_it_layers_on.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_declaring_command_form_peers.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_declaring_command_form_peers.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_declaring_command_form_peers.js +0 -16
- package/dist/esm/for_cratisComponentsPackage/when_declaring_command_form_peers.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_arguments.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_arguments.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_arguments.js +0 -11
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_arguments.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.js +0 -14
- package/dist/esm/for_cratisComponentsPackage/when_describing_query_input_form.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.js +0 -16
- package/dist/esm/for_cratisComponentsPackage/when_describing_single_result.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_resolving_its_dependencies.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_resolving_its_dependencies.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_resolving_its_dependencies.js +0 -49
- package/dist/esm/for_cratisComponentsPackage/when_resolving_its_dependencies.js.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_validating_the_bundle.d.ts +0 -2
- package/dist/esm/for_cratisComponentsPackage/when_validating_the_bundle.d.ts.map +0 -1
- package/dist/esm/for_cratisComponentsPackage/when_validating_the_bundle.js +0 -27
- package/dist/esm/for_cratisComponentsPackage/when_validating_the_bundle.js.map +0 -1
- package/dist/esm/for_properties/when_a_property_has_the_wrong_type.d.ts +0 -2
- package/dist/esm/for_properties/when_a_property_has_the_wrong_type.d.ts.map +0 -1
- package/dist/esm/for_properties/when_a_property_has_the_wrong_type.js +0 -24
- package/dist/esm/for_properties/when_a_property_has_the_wrong_type.js.map +0 -1
- package/dist/esm/for_properties/when_a_property_is_missing.d.ts +0 -2
- package/dist/esm/for_properties/when_a_property_is_missing.d.ts.map +0 -1
- package/dist/esm/for_properties/when_a_property_is_missing.js +0 -13
- package/dist/esm/for_properties/when_a_property_is_missing.js.map +0 -1
- package/dist/esm/for_properties/when_a_property_is_present.d.ts +0 -2
- package/dist/esm/for_properties/when_a_property_is_present.d.ts.map +0 -1
- package/dist/esm/for_properties/when_a_property_is_present.js +0 -21
- package/dist/esm/for_properties/when_a_property_is_present.js.map +0 -1
- package/dist/esm/forms/fields/for_CommandFormField/when_the_field_names_no_property.d.ts +0 -2
- package/dist/esm/forms/fields/for_CommandFormField/when_the_field_names_no_property.d.ts.map +0 -1
- package/dist/esm/forms/fields/for_CommandFormField/when_the_field_names_no_property.js +0 -12
- package/dist/esm/forms/fields/for_CommandFormField/when_the_field_names_no_property.js.map +0 -1
- package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.d.ts +0 -2
- package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.d.ts.map +0 -1
- package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.js +0 -38
- package/dist/esm/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.js.map +0 -1
- package/dist/esm/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.d.ts +0 -2
- package/dist/esm/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.d.ts.map +0 -1
- package/dist/esm/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.js +0 -28
- package/dist/esm/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/given/RecordNote.d.ts +0 -22
- package/dist/esm/forms/for_SceneCommandForm/given/RecordNote.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/given/RecordNote.js +0 -30
- package/dist/esm/forms/for_SceneCommandForm/given/RecordNote.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/given/StartProject.d.ts +0 -32
- package/dist/esm/forms/for_SceneCommandForm/given/StartProject.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/given/StartProject.js +0 -32
- package/dist/esm/forms/for_SceneCommandForm/given/StartProject.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_forwarding_to_the_lazy_runtime.d.ts +0 -2
- package/dist/esm/forms/for_SceneCommandForm/when_forwarding_to_the_lazy_runtime.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_forwarding_to_the_lazy_runtime.js +0 -66
- package/dist/esm/forms/for_SceneCommandForm/when_forwarding_to_the_lazy_runtime.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_native_custom_field_errors_block_submission.d.ts +0 -2
- package/dist/esm/forms/for_SceneCommandForm/when_native_custom_field_errors_block_submission.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_native_custom_field_errors_block_submission.js +0 -26
- package/dist/esm/forms/for_SceneCommandForm/when_native_custom_field_errors_block_submission.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_preserving_guid_error_ownership.d.ts +0 -2
- package/dist/esm/forms/for_SceneCommandForm/when_preserving_guid_error_ownership.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_preserving_guid_error_ownership.js +0 -44
- package/dist/esm/forms/for_SceneCommandForm/when_preserving_guid_error_ownership.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_rendering_submit_in_native_context.d.ts +0 -2
- package/dist/esm/forms/for_SceneCommandForm/when_rendering_submit_in_native_context.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_rendering_submit_in_native_context.js +0 -49
- package/dist/esm/forms/for_SceneCommandForm/when_rendering_submit_in_native_context.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_explicit_inputs.d.ts +0 -2
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_explicit_inputs.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_explicit_inputs.js +0 -176
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_explicit_inputs.js.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_the_native_form.d.ts +0 -2
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_the_native_form.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_the_native_form.js +0 -110
- package/dist/esm/forms/for_SceneCommandForm/when_submitting_the_native_form.js.map +0 -1
- package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.d.ts +0 -26
- package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.js +0 -44
- package/dist/esm/forms/for_SceneQueryInputForm/ProjectLookup.js.map +0 -1
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.d.ts +0 -2
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.js +0 -175
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.js.map +0 -1
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.d.ts +0 -2
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.d.ts.map +0 -1
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.js +0 -413
- package/dist/esm/forms/for_SceneQueryInputForm/when_submitting_native_queries.js.map +0 -1
- package/dist/esm/pages/for_ScenePage/when_rendering_a_page.d.ts +0 -2
- package/dist/esm/pages/for_ScenePage/when_rendering_a_page.d.ts.map +0 -1
- package/dist/esm/pages/for_ScenePage/when_rendering_a_page.js +0 -31
- package/dist/esm/pages/for_ScenePage/when_rendering_a_page.js.map +0 -1
- package/editors/for_SceneObjectNavigationalBar/when_navigating_back_up_the_trail.tsx +0 -24
- package/editors/for_filterDefinitions/when_reading_filter_definitions.ts +0 -49
- package/editors/for_timeMachineVersions/when_reading_versions.ts +0 -39
- package/for_cratisComponents/when_rendering_a_screen_through_the_real_renderer.tsx +0 -37
- package/for_cratisComponentsPackage/when_a_profile_ranks_it_above_the_packages_it_layers_on.ts +0 -49
- package/for_cratisComponentsPackage/when_declaring_command_form_peers.ts +0 -26
- package/for_cratisComponentsPackage/when_describing_query_arguments.ts +0 -15
- package/for_cratisComponentsPackage/when_describing_query_input_form.ts +0 -17
- package/for_cratisComponentsPackage/when_describing_single_result.ts +0 -20
- package/for_cratisComponentsPackage/when_resolving_its_dependencies.ts +0 -78
- package/for_cratisComponentsPackage/when_validating_the_bundle.ts +0 -39
- package/for_properties/when_a_property_has_the_wrong_type.ts +0 -41
- package/for_properties/when_a_property_is_missing.ts +0 -26
- package/for_properties/when_a_property_is_present.ts +0 -36
- package/forms/fields/for_CommandFormField/when_the_field_names_no_property.tsx +0 -16
- package/forms/fields/for_SceneRadioButtonField/when_mapping_the_group_name.tsx +0 -45
- package/forms/fields/for_resolveFieldBinding/when_resolving_a_field_binding.ts +0 -40
- package/forms/for_SceneCommandForm/given/RecordNote.ts +0 -37
- package/forms/for_SceneCommandForm/given/StartProject.ts +0 -41
- package/forms/for_SceneCommandForm/when_forwarding_to_the_lazy_runtime.tsx +0 -69
- package/forms/for_SceneCommandForm/when_native_custom_field_errors_block_submission.tsx +0 -33
- package/forms/for_SceneCommandForm/when_preserving_guid_error_ownership.tsx +0 -50
- package/forms/for_SceneCommandForm/when_rendering_submit_in_native_context.tsx +0 -56
- package/forms/for_SceneCommandForm/when_submitting_explicit_inputs.tsx +0 -189
- package/forms/for_SceneCommandForm/when_submitting_the_native_form.tsx +0 -120
- package/forms/for_SceneQueryInputForm/ProjectLookup.ts +0 -54
- package/forms/for_SceneQueryInputForm/when_submitting_generated_guid_parameters.tsx +0 -188
- package/forms/for_SceneQueryInputForm/when_submitting_native_queries.tsx +0 -423
- package/pages/for_ScenePage/when_rendering_a_page.tsx +0 -39
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
import { act, cleanup, render, screen } from '@testing-library/react';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { externalComponent } from '../../given';
|
|
7
|
-
|
|
8
|
-
// This seam captures adapter inputs only. It does not simulate AutoCommandForm or its footer.
|
|
9
|
-
describe('when forwarding command forms to the lazy runtime', () => {
|
|
10
|
-
const received = vi.fn<(props: Record<string, unknown>) => void>();
|
|
11
|
-
const loaded = vi.fn();
|
|
12
|
-
let bindings: typeof import('../../bindings/bindingRegistry');
|
|
13
|
-
let Form: typeof import('../SceneCommandForm').SceneCommandForm;
|
|
14
|
-
class Original {}
|
|
15
|
-
class Replacement {}
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
vi.resetModules();
|
|
19
|
-
received.mockClear();
|
|
20
|
-
loaded.mockClear();
|
|
21
|
-
vi.doMock('../CommandFormRuntime', () => {
|
|
22
|
-
loaded();
|
|
23
|
-
return { default: (props: Record<string, unknown>) => { received(props); return <span>Runtime boundary</span>; } };
|
|
24
|
-
});
|
|
25
|
-
bindings = await import('../../bindings/bindingRegistry');
|
|
26
|
-
bindings.clearBindings();
|
|
27
|
-
({ SceneCommandForm: Form } = await import('../SceneCommandForm'));
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
cleanup();
|
|
32
|
-
bindings.clearBindings();
|
|
33
|
-
vi.doUnmock('../CommandFormRuntime');
|
|
34
|
-
vi.resetModules();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should preserve the exact registered constructor, exclusions and explicit label', async () => {
|
|
38
|
-
bindings.registerCommand('RecordNote', Original);
|
|
39
|
-
const exclude = ['internalValue'];
|
|
40
|
-
await act(async () => { render(<Form element={externalComponent('Cratis.Components:commandForm', {
|
|
41
|
-
command: 'RecordNote', exclude, submitLabel: 'Record note',
|
|
42
|
-
})} slots={{}} />); });
|
|
43
|
-
received.mock.calls.at(-1)![0].should.deep.equal({ command: Original, exclude, inputs: undefined, submitLabel: 'Record note' });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
for (const submitLabel of [undefined, 42, false]) {
|
|
47
|
-
it(`should default a missing or non-string label (${String(submitLabel)}) to Submit`, async () => {
|
|
48
|
-
bindings.registerCommand('RecordNote', Original);
|
|
49
|
-
await act(async () => { render(<Form element={externalComponent('Cratis.Components:commandForm', {
|
|
50
|
-
command: 'RecordNote', exclude: 'not-an-array', submitLabel,
|
|
51
|
-
})} slots={{}} />); });
|
|
52
|
-
received.mock.calls.at(-1)![0].should.deep.equal({ command: Original, exclude: undefined, inputs: undefined, submitLabel: 'Submit' });
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
it('should preserve legacy last-registration-wins command binding', async () => {
|
|
57
|
-
bindings.registerCommand('RecordNote', Original);
|
|
58
|
-
bindings.registerCommand('RecordNote', Replacement);
|
|
59
|
-
await act(async () => { render(<Form element={externalComponent('Cratis.Components:commandForm', { command: 'RecordNote' })} slots={{}} />); });
|
|
60
|
-
(received.mock.calls.at(-1)![0].command === Replacement).should.equal(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should keep an unbound preview Arc-free even when a query has that name', async () => {
|
|
64
|
-
bindings.registerQuery('RecordNote', Original);
|
|
65
|
-
await act(async () => { render(<Form element={externalComponent('Cratis.Components:commandForm', { command: 'RecordNote' })} slots={{}} />); });
|
|
66
|
-
Boolean(screen.getByText("Unresolved command binding 'RecordNote' on Cratis.Components:commandForm")).should.equal(true);
|
|
67
|
-
loaded.mock.calls.should.have.lengthOf(0);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { CommandForm } from '@cratis/arc.react/commands';
|
|
7
|
-
import { InputTextField } from '@cratis/components/CommandForm';
|
|
8
|
-
import { CommandFormSubmit } from '../CommandFormSubmit';
|
|
9
|
-
import { RecordNote, successfulResponse } from './given/RecordNote';
|
|
10
|
-
|
|
11
|
-
// Independent native prerequisite gate: Arc PR #2724 (expected 22.19.1), not a Scene workaround.
|
|
12
|
-
// This does not exercise or simulate AutoCommandForm's forthcoming footer seam.
|
|
13
|
-
describe('when native custom field errors block submission', () => {
|
|
14
|
-
const fetch = vi.fn<typeof globalThis.fetch>();
|
|
15
|
-
beforeEach(() => { fetch.mockReset(); fetch.mockResolvedValue(successfulResponse()); vi.stubGlobal('fetch', fetch); });
|
|
16
|
-
afterEach(() => { cleanup(); vi.unstubAllGlobals(); });
|
|
17
|
-
|
|
18
|
-
it('should leave submit enabled for feedback but let the native form reject a custom field error', async () => {
|
|
19
|
-
await act(async () => { render(<CommandForm command={RecordNote}
|
|
20
|
-
onFieldValidate={() => 'Subject is rejected by the field'}>
|
|
21
|
-
<InputTextField value={(command: RecordNote) => command.subject} title='Subject' />
|
|
22
|
-
<CommandFormSubmit label='Record note' />
|
|
23
|
-
</CommandForm>); });
|
|
24
|
-
const input = screen.getByRole('textbox', { name: 'Subject' });
|
|
25
|
-
fireEvent.change(input, { target: { value: 'Passes the command validator' } });
|
|
26
|
-
await act(async () => { fireEvent.blur(input); });
|
|
27
|
-
(await screen.findAllByText('Subject is rejected by the field')).length.should.be.greaterThan(0);
|
|
28
|
-
const button = screen.getByRole<HTMLButtonElement>('button', { name: 'Record note' });
|
|
29
|
-
button.disabled.should.equal(false);
|
|
30
|
-
await act(async () => { fireEvent.click(button); });
|
|
31
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
5
|
-
import { CommandForm, useCommandFormContext } from '@cratis/arc.react/commands';
|
|
6
|
-
import { Guid } from '@cratis/fundamentals';
|
|
7
|
-
import { PropertyDescriptor } from '@cratis/arc/reflection';
|
|
8
|
-
import { ExplicitCommandField } from '../ExplicitCommandField';
|
|
9
|
-
import { StartProject } from './given/StartProject';
|
|
10
|
-
|
|
11
|
-
class OptionalProject extends StartProject {
|
|
12
|
-
readonly propertyDescriptors = [new PropertyDescriptor('projectId', Guid, true), new PropertyDescriptor('name', String)];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const input = { property: 'projectId', type: 'guid' as const, label: 'Project ID' };
|
|
16
|
-
|
|
17
|
-
describe('native explicit Guid field error ownership', () => {
|
|
18
|
-
afterEach(cleanup);
|
|
19
|
-
|
|
20
|
-
it('leaves a caller-owned error intact when the invalid field unmounts in the same event', async () => {
|
|
21
|
-
let context!: ReturnType<typeof useCommandFormContext>;
|
|
22
|
-
function Capture() { context = useCommandFormContext(); return null; }
|
|
23
|
-
function form(show: boolean) {
|
|
24
|
-
return <CommandForm command={StartProject}><Capture />{show && <ExplicitCommandField input={input} />}</CommandForm>;
|
|
25
|
-
}
|
|
26
|
-
const result = render(form(true));
|
|
27
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: 'bad' } });
|
|
28
|
-
context.getFieldError('projectId')!.should.equal('Project ID must be a valid GUID');
|
|
29
|
-
await act(async () => {
|
|
30
|
-
context.setCustomFieldError('projectId', 'Caller-owned error');
|
|
31
|
-
result.rerender(form(false));
|
|
32
|
-
});
|
|
33
|
-
context.getFieldError('projectId')!.should.equal('Caller-owned error');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('clears its own malformed optional Guid error on unmount', async () => {
|
|
37
|
-
let context!: ReturnType<typeof useCommandFormContext>;
|
|
38
|
-
function Capture() { context = useCommandFormContext(); return null; }
|
|
39
|
-
function form(show: boolean) {
|
|
40
|
-
return <CommandForm command={OptionalProject}><Capture />{show && <ExplicitCommandField input={input} />}</CommandForm>;
|
|
41
|
-
}
|
|
42
|
-
const result = render(form(true));
|
|
43
|
-
await act(async () => {
|
|
44
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: 'bad' } });
|
|
45
|
-
context.getFieldError('projectId')!.should.equal('Project ID must be a valid GUID');
|
|
46
|
-
result.rerender(form(false));
|
|
47
|
-
});
|
|
48
|
-
(Object.hasOwn(context.customFieldErrors, 'projectId')).should.equal(false);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { CommandForm } from '@cratis/arc.react/commands';
|
|
7
|
-
import { InputTextField } from '@cratis/components/CommandForm';
|
|
8
|
-
import { CommandFormSubmit } from '../CommandFormSubmit';
|
|
9
|
-
import { RecordNote, RestrictedNote, successfulResponse } from './given/RecordNote';
|
|
10
|
-
|
|
11
|
-
// Exercise the private child with REAL Arc context. This does not claim AutoCommandForm footer support.
|
|
12
|
-
describe('when rendering the submit child inside native CommandForm', () => {
|
|
13
|
-
const fetch = vi.fn<typeof globalThis.fetch>();
|
|
14
|
-
beforeEach(() => { fetch.mockReset(); fetch.mockResolvedValue(successfulResponse()); vi.stubGlobal('fetch', fetch); });
|
|
15
|
-
afterEach(() => { cleanup(); vi.unstubAllGlobals(); });
|
|
16
|
-
|
|
17
|
-
it('should let native submit expose invalid untouched fields without HTTP', async () => {
|
|
18
|
-
await act(async () => { render(<CommandForm command={RecordNote}>
|
|
19
|
-
<InputTextField value={(command: RecordNote) => command.subject} title='Subject' />
|
|
20
|
-
<CommandFormSubmit label='Record note' />
|
|
21
|
-
</CommandForm>); });
|
|
22
|
-
const button = screen.getByRole<HTMLButtonElement>('button', { name: 'Record note' });
|
|
23
|
-
button.disabled.should.equal(false);
|
|
24
|
-
await act(async () => { fireEvent.click(button); });
|
|
25
|
-
(await screen.findAllByText('Subject is required')).length.should.be.greaterThan(0);
|
|
26
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should follow native busy state through completion without executing directly', async () => {
|
|
30
|
-
let complete!: (response: Response) => void;
|
|
31
|
-
fetch.mockReturnValueOnce(new Promise<Response>(resolve => { complete = resolve; }));
|
|
32
|
-
await act(async () => { render(<CommandForm command={RecordNote} initialValues={{ subject: 'Valid' }}>
|
|
33
|
-
<CommandFormSubmit label='Record note' />
|
|
34
|
-
</CommandForm>); });
|
|
35
|
-
const button = screen.getByRole<HTMLButtonElement>('button', { name: 'Record note' });
|
|
36
|
-
button.disabled.should.equal(false);
|
|
37
|
-
await act(async () => { fireEvent.click(button); });
|
|
38
|
-
await waitFor(() => button.disabled.should.equal(true));
|
|
39
|
-
button.getAttribute('aria-busy')!.should.equal('true');
|
|
40
|
-
fireEvent.click(button);
|
|
41
|
-
fetch.mock.calls.should.have.lengthOf(1);
|
|
42
|
-
await act(async () => { complete(successfulResponse()); });
|
|
43
|
-
await waitFor(() => button.disabled.should.equal(false));
|
|
44
|
-
button.getAttribute('aria-busy')!.should.equal('false');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should respect native role authorization instead of deriving it in Scene', async () => {
|
|
48
|
-
await act(async () => { render(<CommandForm command={RestrictedNote}>
|
|
49
|
-
<CommandFormSubmit label='Record note' />
|
|
50
|
-
</CommandForm>); });
|
|
51
|
-
const button = screen.getByRole<HTMLButtonElement>('button', { name: 'Record note' });
|
|
52
|
-
button.disabled.should.equal(true);
|
|
53
|
-
fireEvent.click(button);
|
|
54
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { ArcContext } from '@cratis/arc.react';
|
|
7
|
-
import { SceneElementView } from '@cratis/scene.react';
|
|
8
|
-
import { clearBindings, registerCommand } from '../../bindings';
|
|
9
|
-
import { cratisComponents } from '../../cratisComponents';
|
|
10
|
-
import { externalComponent } from '../../given';
|
|
11
|
-
import { AmbiguousProject, OptionalUnsupported, RequiredUnsupported, RestrictedProject, StartProject } from './given/StartProject';
|
|
12
|
-
import { RecordNote, successfulResponse } from './given/RecordNote';
|
|
13
|
-
|
|
14
|
-
const inputs = [{ property: 'projectId', type: 'guid', label: 'Project ID' }, { property: 'name', type: 'string', label: 'Name' }];
|
|
15
|
-
const guid = 'AABBCCDD-1234-5678-9ABC-001122334455';
|
|
16
|
-
|
|
17
|
-
function view(properties: Record<string, unknown> = {}, explicit = true) {
|
|
18
|
-
return <ArcContext.Provider value={{ origin: 'https://example.test', apiBasePath: '/backend', microservice: 'projects', httpHeadersCallback: () => ({ 'x-host': 'scene' }) }}>
|
|
19
|
-
<SceneElementView element={externalComponent('Cratis.Components:commandForm', { command: 'StartProject', ...(explicit ? { inputs } : {}), submitLabel: 'Create project', ...properties })}
|
|
20
|
-
registry={cratisComponents} resolveBinding={() => undefined} />
|
|
21
|
-
</ArcContext.Provider>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('explicit Scene command form using native Arc proxy and execution', () => {
|
|
25
|
-
const fetch = vi.fn<typeof globalThis.fetch>();
|
|
26
|
-
beforeEach(() => { clearBindings(); registerCommand('StartProject', StartProject); fetch.mockReset(); fetch.mockResolvedValue(successfulResponse()); vi.stubGlobal('fetch', fetch); });
|
|
27
|
-
afterEach(() => { cleanup(); clearBindings(); vi.unstubAllGlobals(); });
|
|
28
|
-
|
|
29
|
-
it('rejects untouched, blank and malformed GUIDs, and blank name without HTTP; corrected fields submit the typed payload', async () => {
|
|
30
|
-
render(view());
|
|
31
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Create project' });
|
|
32
|
-
const id = screen.getByRole<HTMLInputElement>('textbox', { name: 'Project ID' });
|
|
33
|
-
const name = screen.getByRole<HTMLInputElement>('textbox', { name: 'Name' });
|
|
34
|
-
(button.form === id.closest('form')).should.equal(true);
|
|
35
|
-
await act(async () => { fireEvent.submit(button.form!); });
|
|
36
|
-
(await screen.findAllByText('Project ID is required')).length.should.be.greaterThan(0);
|
|
37
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
38
|
-
fireEvent.change(id, { target: { value: 'not-a-guid' } });
|
|
39
|
-
fireEvent.change(name, { target: { value: '' } });
|
|
40
|
-
await act(async () => { fireEvent.submit(button.form!); });
|
|
41
|
-
(await screen.findAllByText('Project ID must be a valid GUID')).length.should.be.greaterThan(0);
|
|
42
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
43
|
-
fireEvent.change(id, { target: { value: guid } });
|
|
44
|
-
await act(async () => { fireEvent.submit(button.form!); });
|
|
45
|
-
(await screen.findAllByText('Name is required')).length.should.be.greaterThan(0);
|
|
46
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
47
|
-
fireEvent.change(name, { target: { value: ' My Project ' } });
|
|
48
|
-
await act(async () => { fireEvent.click(button); });
|
|
49
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
50
|
-
const [url, request] = fetch.mock.calls[0];
|
|
51
|
-
String(url).should.equal('https://example.test/backend/synthetic/projects');
|
|
52
|
-
request!.method!.should.equal('POST');
|
|
53
|
-
JSON.parse(String(request!.body)).should.deep.equal({ projectId: guid.toLowerCase(), name: ' My Project ' });
|
|
54
|
-
new Headers(request!.headers).get('x-host')!.should.equal('scene');
|
|
55
|
-
new Headers(request!.headers).get('x-cratis-microservice')!.should.equal('projects');
|
|
56
|
-
fireEvent.change(id, { target: { value: 'broken again' } });
|
|
57
|
-
await act(async () => { fireEvent.submit(button.form!); });
|
|
58
|
-
fetch.mock.calls.should.have.lengthOf(1);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('keeps a typed Guid valid across a label change and executes exactly once', async () => {
|
|
62
|
-
const result = render(view());
|
|
63
|
-
await screen.findByRole('button', { name: 'Create project' });
|
|
64
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: guid } });
|
|
65
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), { target: { value: 'Renamed label' } });
|
|
66
|
-
result.rerender(view({ inputs: [{ ...inputs[0], label: 'Project identifier' }, inputs[1]] }));
|
|
67
|
-
const id = screen.getByRole('textbox', { name: 'Project identifier' });
|
|
68
|
-
(id as HTMLInputElement).value.should.equal(guid);
|
|
69
|
-
await act(async () => { fireEvent.click(screen.getByRole('button', { name: 'Create project' })); });
|
|
70
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
71
|
-
JSON.parse(String(fetch.mock.calls[0][1]!.body)).should.deep.equal({ projectId: guid.toLowerCase(), name: 'Renamed label' });
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('keeps a malformed Guid blocked when its label changes, then submits the corrected typed value', async () => {
|
|
75
|
-
const result = render(view());
|
|
76
|
-
await screen.findByRole('button', { name: 'Create project' });
|
|
77
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), { target: { value: 'After correction' } });
|
|
78
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: 'not-a-guid' } });
|
|
79
|
-
(await screen.findAllByText('Project ID must be a valid GUID')).length.should.be.greaterThan(0);
|
|
80
|
-
result.rerender(view({ inputs: [{ ...inputs[0], label: 'Project identifier' }, inputs[1]] }));
|
|
81
|
-
const id = screen.getByRole<HTMLInputElement>('textbox', { name: 'Project identifier' });
|
|
82
|
-
id.value.should.equal('not-a-guid');
|
|
83
|
-
(await screen.findAllByText('Project identifier must be a valid GUID')).length.should.be.greaterThan(0);
|
|
84
|
-
(screen.queryByText('Project ID must be a valid GUID') === null).should.equal(true);
|
|
85
|
-
await act(async () => { fireEvent.submit(id.closest('form')!); });
|
|
86
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
87
|
-
fireEvent.change(id, { target: { value: guid } });
|
|
88
|
-
await act(async () => { fireEvent.submit(id.closest('form')!); });
|
|
89
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
90
|
-
JSON.parse(String(fetch.mock.calls[0][1]!.body)).should.deep.equal({ projectId: guid.toLowerCase(), name: 'After correction' });
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('remounts the native explicit proxy on a binding switch and same-name hot replacement', async () => {
|
|
94
|
-
class MovedProject extends StartProject { readonly route = '/synthetic/moved-projects'; }
|
|
95
|
-
class HotProject extends StartProject { readonly route = '/synthetic/hot-projects'; }
|
|
96
|
-
registerCommand('MovedProject', MovedProject);
|
|
97
|
-
const result = render(view());
|
|
98
|
-
await screen.findByRole('button', { name: 'Create project' });
|
|
99
|
-
result.rerender(view({ command: 'MovedProject' }));
|
|
100
|
-
await screen.findByRole('textbox', { name: 'Project ID' });
|
|
101
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: guid } });
|
|
102
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), { target: { value: 'Moved' } });
|
|
103
|
-
await act(async () => { fireEvent.click(screen.getByRole('button', { name: 'Create project' })); });
|
|
104
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
105
|
-
String(fetch.mock.calls[0][0]).should.equal('https://example.test/backend/synthetic/moved-projects');
|
|
106
|
-
registerCommand('MovedProject', RestrictedProject);
|
|
107
|
-
result.rerender(view({ command: 'MovedProject' }));
|
|
108
|
-
const denied = await screen.findByRole<HTMLButtonElement>('button', { name: 'Create project' });
|
|
109
|
-
denied.disabled.should.equal(true);
|
|
110
|
-
fireEvent.click(denied);
|
|
111
|
-
fetch.mock.calls.should.have.lengthOf(1);
|
|
112
|
-
registerCommand('MovedProject', HotProject);
|
|
113
|
-
result.rerender(view({ command: 'MovedProject' }));
|
|
114
|
-
const allowed = await screen.findByRole<HTMLButtonElement>('button', { name: 'Create project' });
|
|
115
|
-
allowed.disabled.should.equal(false);
|
|
116
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: guid } });
|
|
117
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), { target: { value: 'Hot' } });
|
|
118
|
-
await act(async () => { fireEvent.click(allowed); });
|
|
119
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(2));
|
|
120
|
-
String(fetch.mock.calls[1][0]).should.equal('https://example.test/backend/synthetic/hot-projects');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('uses native authorization and executing state', async () => {
|
|
124
|
-
registerCommand('StartProject', RestrictedProject);
|
|
125
|
-
render(view());
|
|
126
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Create project' });
|
|
127
|
-
button.disabled.should.equal(true);
|
|
128
|
-
fireEvent.click(button);
|
|
129
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('reflects native execution state and blocks a second button click while pending', async () => {
|
|
133
|
-
let complete!: (response: Response) => void;
|
|
134
|
-
fetch.mockReturnValueOnce(new Promise<Response>(resolve => { complete = resolve; }));
|
|
135
|
-
render(view());
|
|
136
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Create project' });
|
|
137
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Project ID' }), { target: { value: guid } });
|
|
138
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), { target: { value: 'Pending' } });
|
|
139
|
-
await act(async () => { fireEvent.click(button); });
|
|
140
|
-
await waitFor(() => button.disabled.should.equal(true));
|
|
141
|
-
button.getAttribute('aria-busy')!.should.equal('true');
|
|
142
|
-
fireEvent.click(button);
|
|
143
|
-
fetch.mock.calls.should.have.lengthOf(1);
|
|
144
|
-
await act(async () => { complete(successfulResponse()); });
|
|
145
|
-
await waitFor(() => button.disabled.should.equal(false));
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('blocks malformed declarations, unknown names, mismatches, uncovered and ambiguous required descriptors', async () => {
|
|
149
|
-
const invalid = [
|
|
150
|
-
{ inputs: [] }, { inputs: [{ property: 'name', type: 'string', label: 'Name' }, { property: 'name', type: 'string', label: 'Again' }] },
|
|
151
|
-
{ inputs: [{ property: 'PROJECTID', type: 'guid', label: 'ID' }, inputs[1]] },
|
|
152
|
-
{ inputs: [{ ...inputs[0], type: 'string' }, inputs[1]] },
|
|
153
|
-
{ inputs: [inputs[1]] }, { inputs: [{ ...inputs[0], type: 'number' }, inputs[1]] },
|
|
154
|
-
];
|
|
155
|
-
for (const properties of invalid) {
|
|
156
|
-
const result = render(view(properties));
|
|
157
|
-
(await screen.findByRole('alert')).textContent!.length.should.be.greaterThan(0);
|
|
158
|
-
(screen.queryByRole('button', { name: 'Create project' }) === null).should.equal(true);
|
|
159
|
-
result.unmount();
|
|
160
|
-
}
|
|
161
|
-
registerCommand('StartProject', AmbiguousProject);
|
|
162
|
-
render(view());
|
|
163
|
-
(await screen.findByRole('alert')).textContent!.should.contain('Ambiguous');
|
|
164
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('accepts optional unsupported descriptors when unbound but rejects required unsupported descriptors', async () => {
|
|
168
|
-
registerCommand('StartProject', OptionalUnsupported);
|
|
169
|
-
const result = render(view());
|
|
170
|
-
Boolean(await screen.findByRole('button', { name: 'Create project' })).should.equal(true);
|
|
171
|
-
result.unmount();
|
|
172
|
-
registerCommand('StartProject', RequiredUnsupported);
|
|
173
|
-
render(view());
|
|
174
|
-
(await screen.findByRole('alert')).textContent!.should.contain("Required command property 'details'");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('refuses automatic forms with required GUID rather than silently dropping it', async () => {
|
|
178
|
-
render(view({}, false));
|
|
179
|
-
(await screen.findByRole('alert')).textContent!.should.contain("Required command property 'projectId'");
|
|
180
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('does not allow auto exclusions to hide required supported fields', async () => {
|
|
184
|
-
registerCommand('StartProject', RecordNote);
|
|
185
|
-
render(view({ exclude: ['subject'] }, false));
|
|
186
|
-
(await screen.findByRole('alert')).textContent!.should.contain("Required command property 'subject'");
|
|
187
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { ArcContext } from '@cratis/arc.react';
|
|
7
|
-
import { SceneElementView } from '@cratis/scene.react';
|
|
8
|
-
import { clearBindings, registerCommand } from '../../bindings';
|
|
9
|
-
import { cratisComponents } from '../../cratisComponents';
|
|
10
|
-
import { externalComponent } from '../../given';
|
|
11
|
-
import { RecordNote, RestrictedNote, successfulResponse } from './given/RecordNote';
|
|
12
|
-
|
|
13
|
-
function view(properties: Record<string, unknown> = {}) {
|
|
14
|
-
return <ArcContext.Provider value={{ origin: 'https://example.test', apiBasePath: '/backend', microservice: 'notes', httpHeadersCallback: () => ({ 'x-host': 'scene' }) }}>
|
|
15
|
-
<SceneElementView element={externalComponent('Cratis.Components:commandForm', {
|
|
16
|
-
command: 'RecordNote', exclude: ['internalValue'], ...properties,
|
|
17
|
-
})} registry={cratisComponents} resolveBinding={() => undefined} />
|
|
18
|
-
</ArcContext.Provider>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// No Components/form/context doubles. Components <=4.12 ignores footer: these tests MUST fail there,
|
|
22
|
-
// not skip or pass by recreating the forthcoming upstream behavior. Only HTTP is substituted.
|
|
23
|
-
describe('when submitting a Scene command form through native AutoCommandForm', () => {
|
|
24
|
-
const fetch = vi.fn<typeof globalThis.fetch>();
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
clearBindings();
|
|
28
|
-
registerCommand('RecordNote', RecordNote);
|
|
29
|
-
fetch.mockReset();
|
|
30
|
-
fetch.mockResolvedValue(successfulResponse());
|
|
31
|
-
vi.stubGlobal('fetch', fetch);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
afterEach(() => { cleanup(); clearBindings(); vi.unstubAllGlobals(); });
|
|
35
|
-
|
|
36
|
-
it('should submit the actual form with a labeled button and preserve native payload and context', async () => {
|
|
37
|
-
render(view({ submitLabel: 'Record note' }));
|
|
38
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Record note' });
|
|
39
|
-
button.type.should.equal('submit');
|
|
40
|
-
(button.form === screen.getByRole('textbox', { name: 'Subject' }).closest('form')).should.equal(true);
|
|
41
|
-
screen.queryAllByRole('textbox').should.have.lengthOf(1);
|
|
42
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'A synthetic note' } });
|
|
43
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
44
|
-
await act(async () => { fireEvent.click(button); });
|
|
45
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
46
|
-
const [url, request] = fetch.mock.calls[0];
|
|
47
|
-
String(url).should.equal('https://example.test/backend/synthetic/notes');
|
|
48
|
-
request!.method!.should.equal('POST');
|
|
49
|
-
JSON.parse(String(request!.body)).should.deep.equal({ subject: 'A synthetic note', internalValue: 'proxy default' });
|
|
50
|
-
new Headers(request!.headers).get('x-host')!.should.equal('scene');
|
|
51
|
-
new Headers(request!.headers).get('x-cratis-microservice')!.should.equal('notes');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should allow untouched invalid submit to reveal native feedback and correction to execute', async () => {
|
|
55
|
-
render(view());
|
|
56
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Submit' });
|
|
57
|
-
button.disabled.should.equal(false);
|
|
58
|
-
(screen.queryByText('Subject is required') === null).should.equal(true);
|
|
59
|
-
await act(async () => { fireEvent.click(button); });
|
|
60
|
-
(await screen.findAllByText('Subject is required')).length.should.be.greaterThan(0);
|
|
61
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
62
|
-
button.disabled.should.equal(false);
|
|
63
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'Corrected' } });
|
|
64
|
-
await act(async () => { fireEvent.click(button); });
|
|
65
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should reflect native execution until HTTP settles and prevent a second button click', async () => {
|
|
69
|
-
let complete!: (response: Response) => void;
|
|
70
|
-
fetch.mockReturnValueOnce(new Promise<Response>(resolve => { complete = resolve; }));
|
|
71
|
-
render(view());
|
|
72
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Submit' });
|
|
73
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'Pending' } });
|
|
74
|
-
await act(async () => { fireEvent.click(button); });
|
|
75
|
-
await waitFor(() => button.disabled.should.equal(true));
|
|
76
|
-
button.getAttribute('aria-busy')!.should.equal('true');
|
|
77
|
-
fireEvent.click(button);
|
|
78
|
-
fetch.mock.calls.should.have.lengthOf(1);
|
|
79
|
-
await act(async () => { complete(successfulResponse()); });
|
|
80
|
-
await waitFor(() => button.disabled.should.equal(false));
|
|
81
|
-
button.getAttribute('aria-busy')!.should.equal('false');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('remounts the automatic native form on binding switches and same-name hot replacement', async () => {
|
|
85
|
-
class MovedNote extends RecordNote { readonly route = '/synthetic/moved-notes'; }
|
|
86
|
-
class HotNote extends RecordNote { readonly route = '/synthetic/hot-notes'; }
|
|
87
|
-
registerCommand('MovedNote', MovedNote);
|
|
88
|
-
const result = render(view());
|
|
89
|
-
await screen.findByRole('button', { name: 'Submit' });
|
|
90
|
-
result.rerender(view({ command: 'MovedNote' }));
|
|
91
|
-
await screen.findByRole('textbox', { name: 'Subject' });
|
|
92
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'Moved' } });
|
|
93
|
-
await act(async () => { fireEvent.click(screen.getByRole('button', { name: 'Submit' })); });
|
|
94
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(1));
|
|
95
|
-
String(fetch.mock.calls[0][0]).should.equal('https://example.test/backend/synthetic/moved-notes');
|
|
96
|
-
registerCommand('MovedNote', RestrictedNote);
|
|
97
|
-
result.rerender(view({ command: 'MovedNote' }));
|
|
98
|
-
const denied = await screen.findByRole<HTMLButtonElement>('button', { name: 'Submit' });
|
|
99
|
-
denied.disabled.should.equal(true);
|
|
100
|
-
fireEvent.click(denied);
|
|
101
|
-
fetch.mock.calls.should.have.lengthOf(1);
|
|
102
|
-
registerCommand('MovedNote', HotNote);
|
|
103
|
-
result.rerender(view({ command: 'MovedNote' }));
|
|
104
|
-
const allowed = await screen.findByRole<HTMLButtonElement>('button', { name: 'Submit' });
|
|
105
|
-
allowed.disabled.should.equal(false);
|
|
106
|
-
fireEvent.change(screen.getByRole('textbox', { name: 'Subject' }), { target: { value: 'Hot' } });
|
|
107
|
-
await act(async () => { fireEvent.click(allowed); });
|
|
108
|
-
await waitFor(() => fetch.mock.calls.should.have.lengthOf(2));
|
|
109
|
-
String(fetch.mock.calls[1][0]).should.equal('https://example.test/backend/synthetic/hot-notes');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should disable the button when native command authorization denies the identity', async () => {
|
|
113
|
-
registerCommand('RecordNote', RestrictedNote);
|
|
114
|
-
render(view());
|
|
115
|
-
const button = await screen.findByRole<HTMLButtonElement>('button', { name: 'Submit' });
|
|
116
|
-
button.disabled.should.equal(true);
|
|
117
|
-
fireEvent.click(button);
|
|
118
|
-
fetch.mock.calls.should.have.lengthOf(0);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
-
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
-
|
|
4
|
-
// Canonical Projects/Registration/ProjectLookup/ProjectLookup.ts generated consumer shape.
|
|
5
|
-
// Keep Guid parameters, descriptors, model decorators, default value and native methods intact.
|
|
6
|
-
import { QueryFor, QueryResultWithState } from '@cratis/arc/queries';
|
|
7
|
-
import { useQuery, useSuspenseQuery, PerformQuery, SetSorting, QueryWhen } from '@cratis/arc.react/queries';
|
|
8
|
-
import { ParameterDescriptor } from '@cratis/arc/reflection';
|
|
9
|
-
import { Guid, field } from '@cratis/fundamentals';
|
|
10
|
-
|
|
11
|
-
export interface ProjectByIdParameters {
|
|
12
|
-
projectId: Guid;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class ProjectById extends QueryFor<ProjectSummary, ProjectByIdParameters> {
|
|
16
|
-
readonly route: string = '/api/projects/registration/project-lookup/project-by-id';
|
|
17
|
-
readonly queryName: string = 'CanonicalProjects.Projects.Registration.ProjectLookup.ProjectSummary.ProjectById';
|
|
18
|
-
readonly treatWarningsAsErrors: boolean = false;
|
|
19
|
-
readonly roles: string[] = [];
|
|
20
|
-
readonly defaultValue: ProjectSummary = {} as ProjectSummary;
|
|
21
|
-
|
|
22
|
-
constructor() {
|
|
23
|
-
super(ProjectSummary, false);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get requiredRequestParameters(): string[] {
|
|
27
|
-
return ['projectId'];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
readonly parameterDescriptors: ParameterDescriptor[] = [
|
|
31
|
-
new ParameterDescriptor('projectId', Guid, false),
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
projectId!: Guid;
|
|
35
|
-
|
|
36
|
-
static use(args?: ProjectByIdParameters): [QueryResultWithState<ProjectSummary>, PerformQuery<ProjectByIdParameters>, SetSorting] {
|
|
37
|
-
return useQuery<ProjectSummary, ProjectById, ProjectByIdParameters>(ProjectById, args);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
static useSuspense(args?: ProjectByIdParameters): [QueryResultWithState<ProjectSummary>, PerformQuery<ProjectByIdParameters>, SetSorting] {
|
|
41
|
-
return useSuspenseQuery<ProjectSummary, ProjectById, ProjectByIdParameters>(ProjectById, args);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
static when(condition: boolean): QueryWhen<ProjectById, ProjectSummary, ProjectByIdParameters> {
|
|
45
|
-
return new QueryWhen<ProjectById, ProjectSummary, ProjectByIdParameters>(ProjectById, condition);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class ProjectSummary {
|
|
50
|
-
@field(String)
|
|
51
|
-
name!: string;
|
|
52
|
-
@field(Guid)
|
|
53
|
-
projectId!: Guid;
|
|
54
|
-
}
|