@wise/dynamic-flow-client 5.13.2-experimental-e750b58 → 5.13.2
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/build/DynamicFlowCore.js +5 -0
- package/build/common/errorBoundary/ErrorBoundary.js +26 -0
- package/build/common/errorBoundary/ErrorBoundary.test.js +29 -0
- package/build/common/errorBoundary/ErrorBoundaryAlert.js +11 -0
- package/build/common/makeHttpClient/api-utils.js +3 -0
- package/build/common/makeHttpClient/index.js +1 -0
- package/build/common/makeHttpClient/makeHttpClient.js +10 -0
- package/build/common/makeHttpClient/makeHttpClient.test.js +186 -0
- package/build/common/messages/external-confirmation.messages.js +23 -0
- package/build/common/messages/file-upload.messages.js +13 -0
- package/build/common/messages/generic-error.messages.js +18 -0
- package/build/common/messages/help.messages.js +8 -0
- package/build/common/messages/multi-file-upload.messages.js +18 -0
- package/build/common/messages/multi-select.messages.js +8 -0
- package/build/common/messages/paragraph.messages.js +13 -0
- package/build/common/messages/repeatable.messages.js +23 -0
- package/build/common/messages/search.messages.js +8 -0
- package/build/common/messages/validation.array.messages.js +13 -0
- package/build/common/messages/validation.messages.js +53 -0
- package/build/controller/FlowController.js +378 -0
- package/build/controller/executePoll.js +64 -0
- package/build/controller/executeRefresh.js +45 -0
- package/build/controller/executeRequest.js +80 -0
- package/build/controller/executeSubmission.js +70 -0
- package/build/controller/getErrorMessage.js +7 -0
- package/build/controller/getRequestAbortController.js +13 -0
- package/build/controller/getResponseType.js +35 -0
- package/build/controller/getSafeHttpClient.js +8 -0
- package/build/controller/getStepCounter.js +16 -0
- package/build/controller/handleErrorResponse.js +26 -0
- package/build/controller/makeSafeHttpClient.js +8 -0
- package/build/controller/response-utils.js +72 -0
- package/build/controller/response-utils.test.js +52 -0
- package/build/domain/components/AlertComponent.js +1 -0
- package/build/domain/components/AllOfComponent.js +40 -0
- package/build/domain/components/BooleanInputComponent.js +50 -0
- package/build/domain/components/BoxComponent.js +3 -0
- package/build/domain/components/ButtonComponent.js +1 -0
- package/build/domain/components/ColumnsComponent.js +3 -0
- package/build/domain/components/ConstComponent.js +18 -0
- package/build/domain/components/ContainerComponent.js +3 -0
- package/build/domain/components/DateInputComponent.js +75 -0
- package/build/domain/components/DecisionComponent.js +1 -0
- package/build/domain/components/DividerComponent.js +1 -0
- package/build/domain/components/FormComponent.js +3 -0
- package/build/domain/components/FormattedValueComponent.js +44 -0
- package/build/domain/components/HeadingComponent.js +1 -0
- package/build/domain/components/ImageComponent.js +1 -0
- package/build/domain/components/InstructionsComponent.js +1 -0
- package/build/domain/components/IntegerInputComponent.js +74 -0
- package/build/domain/components/ListComponent.js +1 -0
- package/build/domain/components/LoadingIndicatorComponent.js +1 -0
- package/build/domain/components/MarkdownComponent.js +1 -0
- package/build/domain/components/MediaComponent.js +1 -0
- package/build/domain/components/ModalComponent.js +16 -0
- package/build/domain/components/ModalLayoutComponent.js +3 -0
- package/build/domain/components/MoneyInputComponent.js +57 -0
- package/build/domain/components/MultiSelectInputComponent.js +81 -0
- package/build/domain/components/MultiUploadInputComponent.js +88 -0
- package/build/domain/components/NumberInputComponent.js +73 -0
- package/build/domain/components/ObjectComponent.js +45 -0
- package/build/domain/components/ParagraphComponent.js +1 -0
- package/build/domain/components/PersistAsyncComponent.js +92 -0
- package/build/domain/components/ProgressComponent.js +1 -0
- package/build/domain/components/RepeatableComponent.js +103 -0
- package/build/domain/components/ReviewComponent.js +1 -0
- package/build/domain/components/RootDomainComponent.js +173 -0
- package/build/domain/components/SectionComponent.js +5 -0
- package/build/domain/components/SelectInputComponent.js +88 -0
- package/build/domain/components/StatusListComponent.js +1 -0
- package/build/domain/components/SubflowDomainComponent.js +9 -0
- package/build/domain/components/TabsComponent.js +1 -0
- package/build/domain/components/TextInputComponent.js +76 -0
- package/build/domain/components/TupleComponent.js +41 -0
- package/build/domain/components/UploadInputComponent.js +83 -0
- package/build/domain/components/UpsellComponent.js +25 -0
- package/build/domain/components/searchComponent/SearchComponent.js +92 -0
- package/build/domain/components/searchComponent/SearchComponent.test.js +190 -0
- package/build/domain/components/step/ExternalConfirmationComponent.js +24 -0
- package/build/domain/components/step/StepDomainComponent.js +78 -0
- package/build/domain/components/step/ToolbarComponent.js +1 -0
- package/build/domain/components/utils/WithUpdate.js +1 -0
- package/build/domain/components/utils/component-utils.js +12 -0
- package/build/domain/components/utils/debounce.js +34 -0
- package/build/domain/components/utils/debounce.test.js +67 -0
- package/build/domain/components/utils/file-utils.js +21 -0
- package/build/domain/components/utils/file-utils.test.js +27 -0
- package/build/domain/components/utils/getRandomId.js +1 -0
- package/build/domain/components/utils/isExactLocalValueMatch.js +14 -0
- package/build/domain/components/utils/isOrWasValid.js +5 -0
- package/build/domain/components/utils/isPartialModelMatch.js +18 -0
- package/build/domain/components/utils/isPartialModelMatch.test.js +74 -0
- package/build/domain/features/eventNames.js +24 -0
- package/build/domain/features/events.js +1 -0
- package/build/domain/features/persistAsync/getComponentMultiPersistAsync.js +50 -0
- package/build/domain/features/persistAsync/getInitialPersistedState.js +7 -0
- package/build/domain/features/persistAsync/getPerformPersistAsync.js +43 -0
- package/build/domain/features/persistAsync/getPerformPersistAsync.test.js +139 -0
- package/build/domain/features/polling/getStepPolling.js +50 -0
- package/build/domain/features/polling/getStepPolling.test.js +115 -0
- package/build/domain/features/prefetch/getStepPrefetch.js +43 -0
- package/build/domain/features/prefetch/request-cache.js +49 -0
- package/build/domain/features/prefetch/request-cache.test.js +70 -0
- package/build/domain/features/refreshAfter/getStepRefreshAfter.js +24 -0
- package/build/domain/features/refreshAfter/getStepRefreshAfter.test.js +40 -0
- package/build/domain/features/schema-on-change/getDebouncedSchemaOnChange.js +50 -0
- package/build/domain/features/schema-on-change/getSchemaOnChange.js +34 -0
- package/build/domain/features/search/getPerformSearchFunction.js +75 -0
- package/build/domain/features/search/getPerformSearchFunction.test.js +301 -0
- package/build/domain/features/summary/summary-utils.js +40 -0
- package/build/domain/features/summary/summary-utils.test.js +125 -0
- package/build/domain/features/utils/http-utils.js +21 -0
- package/build/domain/features/utils/response-utils.js +9 -0
- package/build/domain/features/validation/spec-utils.js +19 -0
- package/build/domain/features/validation/validateStringPattern.js +24 -0
- package/build/domain/features/validation/validation-functions.js +6 -0
- package/build/domain/features/validation/validation-functions.test.js +108 -0
- package/build/domain/features/validation/value-checks.js +125 -0
- package/build/domain/features/validation/value-checks.test.js +262 -0
- package/build/domain/features/validationAsync/getComponentValidationAsync.js +53 -0
- package/build/domain/features/validationAsync/getComponentValidationAsync.test.js +67 -0
- package/build/domain/features/validationAsync/getInitialValidationAsyncState.js +5 -0
- package/build/domain/features/validationAsync/getPerformValidationAsync.js +45 -0
- package/build/domain/features/validationAsync/getPerformValidationAsync.test.js +143 -0
- package/build/domain/mappers/layout/alertLayoutToComponent.js +16 -0
- package/build/domain/mappers/layout/boxLayoutToComponent.js +13 -0
- package/build/domain/mappers/layout/buttonLayoutToComponent.js +77 -0
- package/build/domain/mappers/layout/columnsLayoutToComponent.js +13 -0
- package/build/domain/mappers/layout/decisionLayoutToComponent.js +22 -0
- package/build/domain/mappers/layout/deprecatedListLayoutToComponent.js +30 -0
- package/build/domain/mappers/layout/dividerLayoutToComponent.js +2 -0
- package/build/domain/mappers/layout/formLayoutToComponent.js +19 -0
- package/build/domain/mappers/layout/headingLayoutToComponent.js +12 -0
- package/build/domain/mappers/layout/imageLayoutToComponent.js +20 -0
- package/build/domain/mappers/layout/infoLayoutToComponent.js +12 -0
- package/build/domain/mappers/layout/instructionsLayoutToComponent.js +12 -0
- package/build/domain/mappers/layout/listLayoutToComponent.js +39 -0
- package/build/domain/mappers/layout/loadingIndicatorLayoutToComponent.js +9 -0
- package/build/domain/mappers/layout/markdownLayoutToComponent.js +12 -0
- package/build/domain/mappers/layout/mediaLayoutToComponent.js +12 -0
- package/build/domain/mappers/layout/modalLayoutToComponent.js +17 -0
- package/build/domain/mappers/layout/modalToComponent.js +8 -0
- package/build/domain/mappers/layout/paragraphLayoutToComponent.js +12 -0
- package/build/domain/mappers/layout/progressLayoutToComponent.js +15 -0
- package/build/domain/mappers/layout/reviewLayoutToComponent.js +48 -0
- package/build/domain/mappers/layout/searchLayoutToComponent.js +44 -0
- package/build/domain/mappers/layout/sectionLayoutToComponent.js +15 -0
- package/build/domain/mappers/layout/statusListLayoutToComponent.js +15 -0
- package/build/domain/mappers/layout/tabsLayoutToComponent.js +16 -0
- package/build/domain/mappers/layout/upsellLayoutToComponent.js +25 -0
- package/build/domain/mappers/mapLayoutToComponent.js +81 -0
- package/build/domain/mappers/mapSchemaToComponent.js +61 -0
- package/build/domain/mappers/mapSchemaToComponent.test.js +112 -0
- package/build/domain/mappers/mapStepSchemas.js +15 -0
- package/build/domain/mappers/mapStepToComponent.js +133 -0
- package/build/domain/mappers/mapStepToComponent.test.js +221 -0
- package/build/domain/mappers/mapToolbarToComponent.js +15 -0
- package/build/domain/mappers/schema/allOfSchemaToComponent.js +16 -0
- package/build/domain/mappers/schema/arraySchemaToComponent/arraySchemaToComponent.js +26 -0
- package/build/domain/mappers/schema/arraySchemaToComponent/arraySchemaToMultiSelectComponent.js +55 -0
- package/build/domain/mappers/schema/arraySchemaToComponent/arraySchemaToMultiUploadComponent.js +67 -0
- package/build/domain/mappers/schema/arraySchemaToComponent/arraySchemaToRepeatableComponent.js +57 -0
- package/build/domain/mappers/schema/arraySchemaToComponent/arraySchemaToTupleComponent.js +20 -0
- package/build/domain/mappers/schema/blobSchemaToComponent.js +15 -0
- package/build/domain/mappers/schema/booleanSchemaToComponent.js +29 -0
- package/build/domain/mappers/schema/constSchemaToComponent.js +23 -0
- package/build/domain/mappers/schema/integerSchemaToComponent.js +28 -0
- package/build/domain/mappers/schema/numberSchemaToComponent.js +26 -0
- package/build/domain/mappers/schema/objectSchemaToComponent/assertDisplayOrder.js +23 -0
- package/build/domain/mappers/schema/objectSchemaToComponent/objectSchemaToFormattedValueComponent.js +9 -0
- package/build/domain/mappers/schema/objectSchemaToComponent/objectSchemaToMoneyInputComponent.js +119 -0
- package/build/domain/mappers/schema/objectSchemaToComponent/objectSchemaToMoneyInputComponent.test.js +96 -0
- package/build/domain/mappers/schema/objectSchemaToComponent/objectSchemaToObjectComponent.js +31 -0
- package/build/domain/mappers/schema/objectSchemaToComponent/objectSchemaToObjectComponent.test.js +99 -0
- package/build/domain/mappers/schema/oneOfSchemaToComponent/oneOfSchemaToComponent.js +66 -0
- package/build/domain/mappers/schema/oneOfSchemaToComponent/oneOfSchemaToComponent.test.js +225 -0
- package/build/domain/mappers/schema/persistAsyncSchemaToComponent.js +29 -0
- package/build/domain/mappers/schema/stringSchemaToComponent/stringSchemaToComponent.js +18 -0
- package/build/domain/mappers/schema/stringSchemaToComponent/stringSchemaToComponent.test.js +133 -0
- package/build/domain/mappers/schema/stringSchemaToComponent/stringSchemaToDateInputComponent.js +48 -0
- package/build/domain/mappers/schema/stringSchemaToComponent/stringSchemaToTextInputComponent.js +38 -0
- package/build/domain/mappers/schema/stringSchemaToComponent/stringSchemaToUploadInputComponent.js +28 -0
- package/build/domain/mappers/schema/tests/test-utils.js +16 -0
- package/build/domain/mappers/schema/types.js +1 -0
- package/build/domain/mappers/schema/utils/getPerformPersistAsyncFn.js +19 -0
- package/build/domain/mappers/schema/utils/getValidationAsyncInitialState.js +23 -0
- package/build/domain/mappers/schema/utils/mapCommonSchemaProps.js +16 -0
- package/build/domain/mappers/types.js +1 -0
- package/build/domain/mappers/utils/FeatureFlags.js +22 -0
- package/build/domain/mappers/utils/behavior-utils.js +44 -0
- package/build/domain/mappers/utils/call-to-action-utils.js +21 -0
- package/build/domain/mappers/utils/getAutocompleteString.js +76 -0
- package/build/domain/mappers/utils/getAutocompleteString.test.js +21 -0
- package/build/domain/mappers/utils/groupLayoutByPinned.js +38 -0
- package/build/domain/mappers/utils/groupLayoutByPinned.test.js +166 -0
- package/build/domain/mappers/utils/image.js +9 -0
- package/build/domain/mappers/utils/layout-utils.js +11 -0
- package/build/domain/mappers/utils/legacy-utils.js +49 -0
- package/build/domain/mappers/utils/media-utils.js +14 -0
- package/build/domain/mappers/utils/suggestions-utils.js +26 -0
- package/build/domain/mappers/utils/suggestions-utils.test.js +36 -0
- package/build/domain/mappers/utils/tags-utils.js +1 -0
- package/build/domain/mappers/utils/utils.js +35 -0
- package/build/domain/types.js +1 -0
- package/build/getSubflowCallbacks.js +38 -0
- package/build/i18n/index.js +40 -0
- package/build/index.js +8 -0
- package/build/main.js +11 -4
- package/build/main.mjs +11 -4
- package/build/renderers/CoreContainerRenderer.js +5 -0
- package/build/renderers/CoreRootRenderer.js +12 -0
- package/build/renderers/EmptyLoadingStateRenderer.js +5 -0
- package/build/renderers/getRenderFunction.js +24 -0
- package/build/renderers/getSchemaErrorMessageFunction.js +97 -0
- package/build/renderers/mappers/alertComponentToProps.js +2 -0
- package/build/renderers/mappers/allOfComponentToProps.js +6 -0
- package/build/renderers/mappers/booleanInputComponentToProps.js +5 -0
- package/build/renderers/mappers/boxComponentToProps.js +13 -0
- package/build/renderers/mappers/buttonComponentToProps.js +4 -0
- package/build/renderers/mappers/columnsComponentToProps.js +11 -0
- package/build/renderers/mappers/componentToRendererProps.js +164 -0
- package/build/renderers/mappers/constComponentToProps.js +5 -0
- package/build/renderers/mappers/containerComponentToProps.js +7 -0
- package/build/renderers/mappers/dateInputComponentToProps.js +2 -0
- package/build/renderers/mappers/decisionComponentToProps.js +16 -0
- package/build/renderers/mappers/dividerComponentToProps.js +2 -0
- package/build/renderers/mappers/externalComponentToProps.js +3 -0
- package/build/renderers/mappers/formComponentToProps.js +12 -0
- package/build/renderers/mappers/formattedValueComponentToProps.js +5 -0
- package/build/renderers/mappers/headingComponentToProps.js +2 -0
- package/build/renderers/mappers/hiddenComponentToProps.js +4 -0
- package/build/renderers/mappers/imageComponentToProps.js +2 -0
- package/build/renderers/mappers/instructionsComponentToProps.js +2 -0
- package/build/renderers/mappers/integerInputComponentToProps.js +2 -0
- package/build/renderers/mappers/listComponentToProps.js +2 -0
- package/build/renderers/mappers/loadingIndicatorComponentToProps.js +2 -0
- package/build/renderers/mappers/markdownComponentToProps.js +2 -0
- package/build/renderers/mappers/mediaComponentToProps.js +2 -0
- package/build/renderers/mappers/modalComponentToProps.js +11 -0
- package/build/renderers/mappers/modalLayoutComponentToProps.js +16 -0
- package/build/renderers/mappers/moneyInputComponentToProps.js +36 -0
- package/build/renderers/mappers/multiSelectComponentToProps.js +23 -0
- package/build/renderers/mappers/multiUploadInputComponentToProps.js +12 -0
- package/build/renderers/mappers/numberInputComponentToProps.js +2 -0
- package/build/renderers/mappers/objectComponentToProps.js +8 -0
- package/build/renderers/mappers/paragraphComponentToProps.js +2 -0
- package/build/renderers/mappers/persistAsyncComponentToProps.js +8 -0
- package/build/renderers/mappers/progressComponentToProps.js +2 -0
- package/build/renderers/mappers/repeatableComponentToProps.js +30 -0
- package/build/renderers/mappers/reviewComponentToProps.js +2 -0
- package/build/renderers/mappers/rootComponentToProps.js +21 -0
- package/build/renderers/mappers/searchComponentToProps.js +57 -0
- package/build/renderers/mappers/sectionComponentToProps.js +6 -0
- package/build/renderers/mappers/selectInputComponentToProps.js +34 -0
- package/build/renderers/mappers/statusListComponentToProps.js +2 -0
- package/build/renderers/mappers/subflowComponentToRendererProps.js +4 -0
- package/build/renderers/mappers/tabsComponentToProps.js +14 -0
- package/build/renderers/mappers/textInputComponentToProps.js +2 -0
- package/build/renderers/mappers/tupleComponentToProps.js +8 -0
- package/build/renderers/mappers/uploadInputComponentToProps.js +8 -0
- package/build/renderers/mappers/upsellComponentToProps.js +2 -0
- package/build/renderers/mappers/utils/getValidationState.js +12 -0
- package/build/renderers/mappers/utils/inputComponentToProps.js +26 -0
- package/build/renderers/mappers/utils/mapErrorsToValidationState.js +9 -0
- package/build/renderers/mappers/utils/pick.js +8 -0
- package/build/renderers/mappers/utils/selectInputOptionsToProps.js +11 -0
- package/build/renderers/stepComponentToProps.js +32 -0
- package/build/renderers/utils.js +69 -0
- package/build/renderers/utils.test.js +70 -0
- package/build/stories/dev-tools/ContainerQueries.story.js +66 -0
- package/build/stories/dev-tools/Debugger.story.js +38 -0
- package/build/stories/dev-tools/FixtureSelect.story.js +23 -0
- package/build/stories/dev-tools/TestServer.story.js +32 -0
- package/build/stories/examples/NativeFlow.story.js +80 -0
- package/build/stories/examples/Recipients.story.js +568 -0
- package/build/stories/spec/behavior/Copy.story.js +71 -0
- package/build/stories/spec/behavior/Link.story.js +40 -0
- package/build/stories/spec/behavior/Modal.story.js +79 -0
- package/build/stories/spec/behavior/Subflow.story.js +267 -0
- package/build/stories/spec/layouts/Decision.story.js +241 -0
- package/build/stories/spec/layouts/Image.Illustrations.Animated.story.js +37 -0
- package/build/stories/spec/layouts/Image.Sizes.story.js +58 -0
- package/build/stories/spec/layouts/Modal.story.js +81 -0
- package/build/stories/spec/layouts/Search.story.js +325 -0
- package/build/stories/spec/layouts/Upsell.story.js +55 -0
- package/build/stories/spec/layouts/button/Button.story.js +100 -0
- package/build/stories/spec/layouts/button/PinnedButton.story.js +81 -0
- package/build/stories/spec/response/ActionResponse.story.js +66 -0
- package/build/stories/spec/schemas/MultiSelect.story.js +148 -0
- package/build/stories/spec/schemas/Upload.story.js +168 -0
- package/build/stories/spec/schemas/const/ConstLayout.story.js +159 -0
- package/build/stories/spec/schemas/const/ObjectConst.story.js +94 -0
- package/build/stories/spec/schemas/features/PersistAsync.story.js +211 -0
- package/build/stories/spec/schemas/features/ValidationAsync.story.js +103 -0
- package/build/stories/spec/schemas/object/FormattedValue.story.js +92 -0
- package/build/stories/spec/schemas/object/MoneyInput.story.js +240 -0
- package/build/stories/spec/schemas/oneOf/OneOfInitialisation.story.js +55 -0
- package/build/stories/spec/schemas/oneOf/OneOfWithSingleOption.story.js +114 -0
- package/build/stories/spec/step/Controls.story.js +109 -0
- package/build/stories/spec/step/DFModal.story.js +58 -0
- package/build/stories/spec/step/Footer.story.js +70 -0
- package/build/stories/spec/step/Navigation.story.js +20 -0
- package/build/stories/spec/step/ScrollToBottom.story.js +103 -0
- package/build/stories/spec/step/Tags.story.js +39 -0
- package/build/stories/spec/step/ToolBar.story.js +60 -0
- package/build/stories/spec/step/features/ErrorHandling.story.js +92 -0
- package/build/stories/spec/step/features/External.story.js +91 -0
- package/build/stories/spec/step/features/Polling.story.js +108 -0
- package/build/stories/spec/step/features/RefreshAfter.story.js +92 -0
- package/build/stories/spec/step/features/refresh/Refresh.story.js +258 -0
- package/build/stories/spec/step/features/refresh/RefreshWithPersistAsync.story.js +958 -0
- package/build/stories/types.js +1 -0
- package/build/stories/utils/fixtureHttpClient.js +70 -0
- package/build/stories/utils/getBasicStep.js +223 -0
- package/build/stories/utils/mockSearchHandler.js +71 -0
- package/build/stories/utils/render-utils.js +41 -0
- package/build/stories/visual-tests/layouts/NotUsingListItem.story.js +17 -0
- package/build/test-utils/DynamicFlowWise.js +37 -0
- package/build/test-utils/DynamicFlowWiseModal.js +34 -0
- package/build/test-utils/NeptuneProviders.js +11 -0
- package/build/test-utils/component-utils.js +5 -0
- package/build/test-utils/fetch-utils.js +45 -0
- package/build/test-utils/getMergedTestRenderers.js +34 -0
- package/build/test-utils/getRandomId.js +1 -0
- package/build/test-utils/index.js +3 -0
- package/build/test-utils/openLinkInNewTab.js +15 -0
- package/build/test-utils/rtl-utils.js +7 -0
- package/build/test-utils/step-utils.js +6 -0
- package/build/test-utils/wait.js +3 -0
- package/build/tests/AlertLayout.test.js +78 -0
- package/build/tests/ArrayTuple.test.js +118 -0
- package/build/tests/ButtonLayout.test.js +308 -0
- package/build/tests/ConstLayout.test.js +95 -0
- package/build/tests/DateInput.test.js +163 -0
- package/build/tests/DecisionLayout.test.js +146 -0
- package/build/tests/DynamicFlow.test.js +147 -0
- package/build/tests/External.test.js +169 -0
- package/build/tests/Flow.test.js +328 -0
- package/build/tests/FormLayout.test.js +28 -0
- package/build/tests/FormattedValue.test.js +107 -0
- package/build/tests/ImageRenderer.test.js +78 -0
- package/build/tests/InitialAction.test.js +179 -0
- package/build/tests/InitialStep.test.js +168 -0
- package/build/tests/InstructionsLayout.test.js +45 -0
- package/build/tests/ListLayout.test.js +168 -0
- package/build/tests/Logging.test.js +53 -0
- package/build/tests/ModalBehavior.test.js +149 -0
- package/build/tests/MoneyInput.test.js +316 -0
- package/build/tests/MultiUpload.test.js +293 -0
- package/build/tests/NativeBack.test.js +267 -0
- package/build/tests/NoOp.test.js +194 -0
- package/build/tests/OneOfInitialisation.test.js +571 -0
- package/build/tests/PersistAsync.test.js +687 -0
- package/build/tests/Polling.test.js +702 -0
- package/build/tests/Prefetching.test.js +230 -0
- package/build/tests/RefreshAfter.test.js +63 -0
- package/build/tests/RefreshOnChange.ResponseHandling.test.js +205 -0
- package/build/tests/RefreshOnChange.test.js +233 -0
- package/build/tests/RefreshOnChange.with.Segmented.test.js +350 -0
- package/build/tests/RefreshOnChange.with.Tabs.test.js +358 -0
- package/build/tests/RefreshOnChangePreserve.test.js +224 -0
- package/build/tests/RendererProps.test.js +361 -0
- package/build/tests/Repeatable.test.js +107 -0
- package/build/tests/Rerendering.test.js +67 -0
- package/build/tests/ReviewLayout.test.js +274 -0
- package/build/tests/SchemaOnChange.test.js +133 -0
- package/build/tests/SchemaReferences.test.js +88 -0
- package/build/tests/ScrollToBottom.test.js +122 -0
- package/build/tests/ScrollToError.test.js +217 -0
- package/build/tests/SegmentedControl.test.js +49 -0
- package/build/tests/SingleFileUpload.test.js +168 -0
- package/build/tests/StatusList.test.js +85 -0
- package/build/tests/Subflow.test.js +887 -0
- package/build/tests/Submission.ResponseHandling.test.js +557 -0
- package/build/tests/Submission.merging.test.js +202 -0
- package/build/tests/Submission.test.js +748 -0
- package/build/tests/Tags.test.js +475 -0
- package/build/tests/Upsell.test.js +154 -0
- package/build/tests/ValidationAsync.test.js +295 -0
- package/build/tests/legacy/Actions.test.js +158 -0
- package/build/tests/legacy/BackButton.test.js +114 -0
- package/build/tests/legacy/HiddenSchemas.test.js +246 -0
- package/build/tests/legacy/MultiSelect.test.js +501 -0
- package/build/tests/legacy/MultipleFileUploadSchema.test.js +350 -0
- package/build/tests/legacy/PersistAsync.blob-schema.test.js +224 -0
- package/build/tests/legacy/PersistAsync.string-schema.test.js +211 -0
- package/build/tests/legacy/RefreshStepOnChange.debouncing.test.js +209 -0
- package/build/tests/legacy/RefreshStepOnChange.test.js +424 -0
- package/build/tests/legacy/Search.test.js +439 -0
- package/build/tests/renderers/MultiSelectInputRendererProps.test.js +58 -0
- package/build/tests/renderers/SelectInputRendererProps.test.js +43 -0
- package/build/tests/renderers/TextInputRenderer.test.js +51 -0
- package/build/tsconfig.types.tsbuildinfo +1 -1
- package/build/types/domain/features/polling/getStepPolling.d.ts.map +1 -1
- package/build/types.js +1 -0
- package/build/useDynamicFlow.js +105 -0
- package/build/useDynamicFlowModal.js +58 -0
- package/build/utils/analyse-step.js +14 -0
- package/build/utils/component-utils.js +8 -0
- package/build/utils/component-utils.test.js +113 -0
- package/build/utils/getScrollToTop.js +12 -0
- package/build/utils/normalise-flow-id.js +1 -0
- package/build/utils/normalise-flow-id.test.js +24 -0
- package/build/utils/recursiveMerge.js +40 -0
- package/build/utils/recursiveMerge.test.js +93 -0
- package/build/utils/type-utils.js +21 -0
- package/build/utils/type-validators.js +11 -0
- package/build/utils/type-validators.test.js +180 -0
- package/build/utils/useStableCallback.js +15 -0
- package/package.json +37 -38
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isNullish } from '../../../utils/type-validators';
|
|
2
|
+
import { getRandomId } from '../../components/utils/getRandomId';
|
|
3
|
+
/**
|
|
4
|
+
* Creates an onPersistAsync handler for a component.
|
|
5
|
+
*/
|
|
6
|
+
export const getComponentMultiPersistAsync = (update, performPersistAsync) =>
|
|
7
|
+
/**
|
|
8
|
+
* Will update the persistedState when a new request is made, and will update
|
|
9
|
+
* the value or set errors when the request completes.
|
|
10
|
+
*/
|
|
11
|
+
async (component, index, value) => {
|
|
12
|
+
if (isNullish(value)) {
|
|
13
|
+
throw new Error('Value must be a file or base64 string.');
|
|
14
|
+
}
|
|
15
|
+
const newAbortController = new AbortController();
|
|
16
|
+
const { signal } = newAbortController;
|
|
17
|
+
const newSubmission = performPersistAsync({ value, signal })
|
|
18
|
+
.then((newValue) => {
|
|
19
|
+
update(component, (draft) => {
|
|
20
|
+
draft.persistedState[index].lastResponse = newValue;
|
|
21
|
+
});
|
|
22
|
+
return newValue;
|
|
23
|
+
})
|
|
24
|
+
.catch((error) => {
|
|
25
|
+
update(component, (draft) => {
|
|
26
|
+
// The file was not persisted, so delete it
|
|
27
|
+
draft.persistedState = [
|
|
28
|
+
...draft.persistedState.slice(0, index),
|
|
29
|
+
...draft.persistedState.slice(index + 1),
|
|
30
|
+
];
|
|
31
|
+
draft.value.splice(index, 1);
|
|
32
|
+
draft.files.splice(index, 1);
|
|
33
|
+
});
|
|
34
|
+
throw error;
|
|
35
|
+
});
|
|
36
|
+
update(component, (draft) => {
|
|
37
|
+
draft.persistedState = [
|
|
38
|
+
...draft.persistedState.slice(0, index),
|
|
39
|
+
{
|
|
40
|
+
id: getRandomId(),
|
|
41
|
+
abortController: newAbortController,
|
|
42
|
+
lastResponse: null,
|
|
43
|
+
lastSubmitted: null,
|
|
44
|
+
submission: newSubmission,
|
|
45
|
+
},
|
|
46
|
+
...draft.persistedState.slice(index),
|
|
47
|
+
];
|
|
48
|
+
});
|
|
49
|
+
return newSubmission;
|
|
50
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { isNullish } from '../../../utils/type-validators';
|
|
2
|
+
export const getInitialPersistedState = (lastSubmitted, model) => ({
|
|
3
|
+
abortController: new AbortController(),
|
|
4
|
+
lastSubmitted: !isNullish(model) ? (lastSubmitted !== null && lastSubmitted !== void 0 ? lastSubmitted : null) : null,
|
|
5
|
+
lastResponse: model !== null && model !== void 0 ? model : null,
|
|
6
|
+
submission: Promise.resolve(model !== null && model !== void 0 ? model : null),
|
|
7
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isObject, isString } from '../../../utils/type-validators';
|
|
2
|
+
import { constructPayload } from '../utils/http-utils';
|
|
3
|
+
import { getAnalyticsFromErrorResponse } from '../utils/response-utils';
|
|
4
|
+
/**
|
|
5
|
+
* Takes an httpClient and schema persist async config and returns a function that can
|
|
6
|
+
* execute a persist async request.
|
|
7
|
+
*/
|
|
8
|
+
export const getPerformPersistAsync = ({ genericErrorMessage, httpClient, persistAsyncConfig, schemaId, logEvent, trackEvent, }) => {
|
|
9
|
+
const { idProperty, param, method, url } = persistAsyncConfig;
|
|
10
|
+
const trackFailure = (json) => {
|
|
11
|
+
const analytics = getAnalyticsFromErrorResponse(json);
|
|
12
|
+
trackEvent('PersistAsync Failed', Object.assign({ schema: schemaId }, analytics));
|
|
13
|
+
};
|
|
14
|
+
return async function performPersistAsync({ value, signal }) {
|
|
15
|
+
let response;
|
|
16
|
+
let json;
|
|
17
|
+
try {
|
|
18
|
+
trackEvent('PersistAsync Triggered', { schema: schemaId });
|
|
19
|
+
response = await httpClient(url, constructPayload({ value, signal, requestConfig: { method, param } }));
|
|
20
|
+
json = (await response.json());
|
|
21
|
+
if (response.ok && isObject(json)) {
|
|
22
|
+
trackEvent('PersistAsync Succeeded', { schema: schemaId });
|
|
23
|
+
if (json[idProperty] === undefined) {
|
|
24
|
+
logEvent('error', `Response from persist async did not contain expected property ${idProperty}.`);
|
|
25
|
+
throw new Error(genericErrorMessage);
|
|
26
|
+
}
|
|
27
|
+
return json[idProperty];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (_a) {
|
|
31
|
+
trackFailure();
|
|
32
|
+
throw new Error(genericErrorMessage);
|
|
33
|
+
}
|
|
34
|
+
const validationError = !response.ok && isObject(json) ? getValidationError(param, json) : null;
|
|
35
|
+
trackFailure(json);
|
|
36
|
+
throw new Error(validationError !== null && validationError !== void 0 ? validationError : genericErrorMessage);
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const getValidationError = (param, response) => {
|
|
40
|
+
var _a;
|
|
41
|
+
const message = (_a = response.validation) === null || _a === void 0 ? void 0 : _a[param];
|
|
42
|
+
return isString(message) ? message : null;
|
|
43
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { getMockHttpClient, respondWith } from '../../../test-utils';
|
|
3
|
+
import { getPerformPersistAsync } from './getPerformPersistAsync';
|
|
4
|
+
describe('getPerformPersistAsync', () => {
|
|
5
|
+
const genericErrorMessage = 'Error';
|
|
6
|
+
const trackEvent = vi.fn();
|
|
7
|
+
const logEvent = vi.fn();
|
|
8
|
+
const signal = {};
|
|
9
|
+
const persistAsyncConfig = {
|
|
10
|
+
idProperty: 'id',
|
|
11
|
+
method: 'POST',
|
|
12
|
+
param: 'param',
|
|
13
|
+
url: '/persist',
|
|
14
|
+
};
|
|
15
|
+
it('should make the request using the specified parameter', async () => {
|
|
16
|
+
const spyHttpClient = vi.fn().mockResolvedValue(respondWith({ id: 1 }));
|
|
17
|
+
const spyTrackEvent = vi.fn();
|
|
18
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
19
|
+
httpClient: spyHttpClient,
|
|
20
|
+
trackEvent: spyTrackEvent,
|
|
21
|
+
logEvent,
|
|
22
|
+
persistAsyncConfig,
|
|
23
|
+
genericErrorMessage,
|
|
24
|
+
schemaId: 'schemaId',
|
|
25
|
+
});
|
|
26
|
+
await performPersistAsync({ value: 'value', signal });
|
|
27
|
+
expect(spyHttpClient).toHaveBeenCalledWith('/persist', {
|
|
28
|
+
body: '{"param":"value"}',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
method: 'POST',
|
|
31
|
+
signal,
|
|
32
|
+
});
|
|
33
|
+
expect(spyTrackEvent).toHaveBeenCalledWith('PersistAsync Triggered', expect.objectContaining({ schema: 'schemaId' }));
|
|
34
|
+
});
|
|
35
|
+
it('should wrap blobs in form data before submitting', async () => {
|
|
36
|
+
const spyHttpClient = vi.fn().mockResolvedValue(respondWith({ id: 1 }));
|
|
37
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
38
|
+
httpClient: spyHttpClient,
|
|
39
|
+
trackEvent,
|
|
40
|
+
logEvent,
|
|
41
|
+
persistAsyncConfig,
|
|
42
|
+
genericErrorMessage,
|
|
43
|
+
});
|
|
44
|
+
const file = new File(['file'], 'file.txt');
|
|
45
|
+
await performPersistAsync({ value: file, signal });
|
|
46
|
+
expect(spyHttpClient).toHaveBeenCalledWith('/persist', {
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
48
|
+
body: expect.any(FormData),
|
|
49
|
+
headers: {},
|
|
50
|
+
method: 'POST',
|
|
51
|
+
signal,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('when the response is ok', () => {
|
|
55
|
+
it('should read response value from idProperty', async () => {
|
|
56
|
+
const httpClient = getMockHttpClient({
|
|
57
|
+
'/persist': async () => respondWith({ id: 1 }),
|
|
58
|
+
});
|
|
59
|
+
const spyTrackEvent = vi.fn();
|
|
60
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
61
|
+
httpClient,
|
|
62
|
+
trackEvent: spyTrackEvent,
|
|
63
|
+
logEvent,
|
|
64
|
+
persistAsyncConfig,
|
|
65
|
+
genericErrorMessage,
|
|
66
|
+
schemaId: 'schemaId',
|
|
67
|
+
});
|
|
68
|
+
const result = await performPersistAsync({ value: 'value', signal });
|
|
69
|
+
expect(result).toBe(1);
|
|
70
|
+
expect(spyTrackEvent).toHaveBeenLastCalledWith('PersistAsync Succeeded', expect.objectContaining({ schema: 'schemaId' }));
|
|
71
|
+
});
|
|
72
|
+
it('should throw a generic error if the response is not an object', async () => {
|
|
73
|
+
const httpClient = getMockHttpClient({
|
|
74
|
+
'/persist': async () => respondWith(1),
|
|
75
|
+
});
|
|
76
|
+
const spyTrackEvent = vi.fn();
|
|
77
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
78
|
+
httpClient,
|
|
79
|
+
trackEvent: spyTrackEvent,
|
|
80
|
+
logEvent,
|
|
81
|
+
persistAsyncConfig,
|
|
82
|
+
genericErrorMessage,
|
|
83
|
+
schemaId: 'schemaId',
|
|
84
|
+
});
|
|
85
|
+
await expect(performPersistAsync({ value: 'value', signal })).rejects.toThrow(genericErrorMessage);
|
|
86
|
+
expect(spyTrackEvent).toHaveBeenLastCalledWith('PersistAsync Failed', expect.objectContaining({ schema: 'schemaId' }));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe.each([[401], [422], [504]])('when the response is %s', (errorStatusCode) => {
|
|
90
|
+
it('should throw if the response includes a validation message', async () => {
|
|
91
|
+
const httpClient = getMockHttpClient({
|
|
92
|
+
'/persist': async () => respondWith({ validation: { param: 'error with this field' } }, { status: errorStatusCode }),
|
|
93
|
+
});
|
|
94
|
+
const spyTrackEvent = vi.fn();
|
|
95
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
96
|
+
httpClient,
|
|
97
|
+
trackEvent: spyTrackEvent,
|
|
98
|
+
logEvent,
|
|
99
|
+
persistAsyncConfig,
|
|
100
|
+
genericErrorMessage,
|
|
101
|
+
schemaId: 'schemaId',
|
|
102
|
+
});
|
|
103
|
+
await expect(performPersistAsync({ value: 'value', signal })).rejects.toThrow('error with this field');
|
|
104
|
+
expect(spyTrackEvent).toHaveBeenLastCalledWith('PersistAsync Failed', expect.objectContaining({ schema: 'schemaId' }));
|
|
105
|
+
});
|
|
106
|
+
it('should include analytics if passed', async () => {
|
|
107
|
+
const httpClient = getMockHttpClient({
|
|
108
|
+
'/persist': async () => respondWith({ validation: { param: 'error with this field' }, analytics: { some: 'extras' } }, { status: errorStatusCode }),
|
|
109
|
+
});
|
|
110
|
+
const spyTrackEvent = vi.fn();
|
|
111
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
112
|
+
httpClient,
|
|
113
|
+
trackEvent: spyTrackEvent,
|
|
114
|
+
logEvent,
|
|
115
|
+
persistAsyncConfig,
|
|
116
|
+
genericErrorMessage,
|
|
117
|
+
schemaId: 'schemaId',
|
|
118
|
+
});
|
|
119
|
+
await expect(performPersistAsync({ value: 'value', signal })).rejects.toThrow('error with this field');
|
|
120
|
+
expect(spyTrackEvent).toHaveBeenLastCalledWith('PersistAsync Failed', expect.objectContaining({ schema: 'schemaId', some: 'extras' }));
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
it('should throw a generic error if request fails', async () => {
|
|
124
|
+
const httpClient = getMockHttpClient({
|
|
125
|
+
'/persist': async () => Promise.reject(new Error()),
|
|
126
|
+
});
|
|
127
|
+
const spyTrackEvent = vi.fn();
|
|
128
|
+
const performPersistAsync = getPerformPersistAsync({
|
|
129
|
+
httpClient,
|
|
130
|
+
trackEvent: spyTrackEvent,
|
|
131
|
+
logEvent,
|
|
132
|
+
persistAsyncConfig,
|
|
133
|
+
genericErrorMessage,
|
|
134
|
+
schemaId: 'schemaId',
|
|
135
|
+
});
|
|
136
|
+
await expect(performPersistAsync({ value: 'value', signal })).rejects.toThrow(genericErrorMessage);
|
|
137
|
+
expect(spyTrackEvent).toHaveBeenLastCalledWith('PersistAsync Failed', expect.objectContaining({ schema: 'schemaId' }));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getDomainLayerBehavior } from '../../mappers/utils/behavior-utils';
|
|
2
|
+
export const getStepPolling = ({ pollingConfig, logEvent, onBehavior, onPoll, registerSubmissionBehavior, }) => {
|
|
3
|
+
const { interval, delay = interval, maxAttempts, url, onError } = pollingConfig;
|
|
4
|
+
let abortController = new AbortController();
|
|
5
|
+
let intervalRef = null;
|
|
6
|
+
let hasCompleted = false;
|
|
7
|
+
if (delay == null) {
|
|
8
|
+
throw new Error('Polling configuration must include delay or interval');
|
|
9
|
+
}
|
|
10
|
+
const onErrorBehavior = getDomainLayerBehavior(onError, [], registerSubmissionBehavior);
|
|
11
|
+
let attempts = 0;
|
|
12
|
+
const poll = () => {
|
|
13
|
+
attempts += 1;
|
|
14
|
+
abortController.abort();
|
|
15
|
+
abortController = new AbortController();
|
|
16
|
+
const { signal } = abortController;
|
|
17
|
+
onPoll(url, onErrorBehavior, signal)
|
|
18
|
+
.then((result) => {
|
|
19
|
+
if (result) {
|
|
20
|
+
complete();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (attempts >= maxAttempts && !signal.aborted) {
|
|
24
|
+
complete();
|
|
25
|
+
void onBehavior(onErrorBehavior);
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.catch(() => { });
|
|
29
|
+
};
|
|
30
|
+
const complete = () => {
|
|
31
|
+
hasCompleted = true;
|
|
32
|
+
stop();
|
|
33
|
+
};
|
|
34
|
+
const start = () => {
|
|
35
|
+
if (hasCompleted) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
intervalRef = setInterval(poll, delay * 1000);
|
|
39
|
+
poll();
|
|
40
|
+
};
|
|
41
|
+
const stop = () => {
|
|
42
|
+
if (!intervalRef) {
|
|
43
|
+
logEvent('warning', 'Attempted to stop polling but it was not started');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
clearInterval(intervalRef);
|
|
47
|
+
abortController.abort();
|
|
48
|
+
};
|
|
49
|
+
return { start, stop };
|
|
50
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getStepPolling } from './getStepPolling';
|
|
2
|
+
import { vi } from 'vitest';
|
|
3
|
+
describe('getStepPolling', () => {
|
|
4
|
+
it('when called will start polling', async () => {
|
|
5
|
+
const onPoll = vi.fn().mockImplementation(async () => Promise.resolve());
|
|
6
|
+
getStepPolling({
|
|
7
|
+
pollingConfig: {
|
|
8
|
+
url: '/polling',
|
|
9
|
+
maxAttempts: 5,
|
|
10
|
+
interval: 1,
|
|
11
|
+
onError: { action: { url: '/failure' } },
|
|
12
|
+
},
|
|
13
|
+
logEvent: vi.fn(),
|
|
14
|
+
onBehavior: vi.fn(),
|
|
15
|
+
onPoll,
|
|
16
|
+
registerSubmissionBehavior: vi.fn(),
|
|
17
|
+
}).start();
|
|
18
|
+
expect(onPoll).toHaveBeenCalledTimes(1);
|
|
19
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
20
|
+
expect(onPoll).toHaveBeenCalledTimes(2);
|
|
21
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
22
|
+
expect(onPoll).toHaveBeenCalledTimes(3);
|
|
23
|
+
});
|
|
24
|
+
it('the interval between polls is defined by the pollingConfig', async () => {
|
|
25
|
+
const onPoll = vi.fn().mockImplementation(async () => Promise.resolve());
|
|
26
|
+
const interval = 4;
|
|
27
|
+
getStepPolling({
|
|
28
|
+
pollingConfig: {
|
|
29
|
+
url: '/polling',
|
|
30
|
+
maxAttempts: 5,
|
|
31
|
+
interval,
|
|
32
|
+
onError: { action: { url: '/failure' } },
|
|
33
|
+
},
|
|
34
|
+
logEvent: vi.fn(),
|
|
35
|
+
onBehavior: vi.fn(),
|
|
36
|
+
onPoll,
|
|
37
|
+
registerSubmissionBehavior: vi.fn(),
|
|
38
|
+
}).start();
|
|
39
|
+
expect(onPoll).toHaveBeenCalledTimes(1);
|
|
40
|
+
await vi.advanceTimersByTimeAsync(interval * 1000);
|
|
41
|
+
expect(onPoll).toHaveBeenCalledTimes(2);
|
|
42
|
+
await vi.advanceTimersByTimeAsync(interval * 1000);
|
|
43
|
+
expect(onPoll).toHaveBeenCalledTimes(3);
|
|
44
|
+
});
|
|
45
|
+
it('will only poll a number of times defined by maxAttempts - and will trigger the onError action if it reaches this without a valid response', async () => {
|
|
46
|
+
const onPoll = vi.fn().mockImplementation(async () => Promise.resolve(false));
|
|
47
|
+
const onBehavior = vi.fn();
|
|
48
|
+
getStepPolling({
|
|
49
|
+
pollingConfig: {
|
|
50
|
+
url: '/polling',
|
|
51
|
+
maxAttempts: 5,
|
|
52
|
+
interval: 1,
|
|
53
|
+
onError: { action: { url: '/failure' } },
|
|
54
|
+
},
|
|
55
|
+
logEvent: vi.fn(),
|
|
56
|
+
onBehavior,
|
|
57
|
+
onPoll,
|
|
58
|
+
registerSubmissionBehavior: vi.fn(),
|
|
59
|
+
}).start();
|
|
60
|
+
await vi.advanceTimersByTimeAsync(5000);
|
|
61
|
+
expect(onPoll).toHaveBeenCalledTimes(5);
|
|
62
|
+
expect(onBehavior).toHaveBeenCalledTimes(1);
|
|
63
|
+
expect(onBehavior).toHaveBeenCalledWith(expect.objectContaining({
|
|
64
|
+
type: 'action',
|
|
65
|
+
action: expect.objectContaining({ url: '/failure' }),
|
|
66
|
+
}));
|
|
67
|
+
});
|
|
68
|
+
it('will stop polling once a flow response is received, and it will not restart polling if start is called again', async () => {
|
|
69
|
+
const onPoll = vi.fn().mockImplementation(async () => {
|
|
70
|
+
// Simulate receiving a flow response, which should stop polling and resolve to true
|
|
71
|
+
return Promise.resolve(true);
|
|
72
|
+
});
|
|
73
|
+
const polling = getStepPolling({
|
|
74
|
+
pollingConfig: {
|
|
75
|
+
url: '/polling',
|
|
76
|
+
maxAttempts: 5,
|
|
77
|
+
interval: 1,
|
|
78
|
+
onError: { action: { url: '/failure' } },
|
|
79
|
+
},
|
|
80
|
+
logEvent: vi.fn(),
|
|
81
|
+
onBehavior: vi.fn(),
|
|
82
|
+
onPoll,
|
|
83
|
+
registerSubmissionBehavior: vi.fn(),
|
|
84
|
+
});
|
|
85
|
+
polling.start();
|
|
86
|
+
expect(onPoll).toHaveBeenCalledTimes(1);
|
|
87
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
88
|
+
expect(onPoll).toHaveBeenCalledTimes(1);
|
|
89
|
+
polling.start();
|
|
90
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
91
|
+
expect(onPoll).toHaveBeenCalledTimes(1);
|
|
92
|
+
});
|
|
93
|
+
it('returns an object with a function that stops polling', async () => {
|
|
94
|
+
const onPoll = vi.fn().mockImplementation(async () => Promise.resolve());
|
|
95
|
+
const { start, stop } = getStepPolling({
|
|
96
|
+
pollingConfig: {
|
|
97
|
+
url: '/polling',
|
|
98
|
+
maxAttempts: 5,
|
|
99
|
+
interval: 1,
|
|
100
|
+
onError: { action: { url: '/failure' } },
|
|
101
|
+
},
|
|
102
|
+
logEvent: vi.fn(),
|
|
103
|
+
onBehavior: vi.fn(),
|
|
104
|
+
onPoll,
|
|
105
|
+
registerSubmissionBehavior: vi.fn(),
|
|
106
|
+
});
|
|
107
|
+
start();
|
|
108
|
+
expect(onPoll).toHaveBeenCalledTimes(1);
|
|
109
|
+
await vi.advanceTimersByTimeAsync(2000);
|
|
110
|
+
expect(onPoll).toHaveBeenCalledTimes(3);
|
|
111
|
+
stop();
|
|
112
|
+
await vi.advanceTimersByTimeAsync(2000);
|
|
113
|
+
expect(onPoll).toHaveBeenCalledTimes(3);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createRequestFromAction } from '../../../controller/executeSubmission';
|
|
2
|
+
import { makeRequestCacheKey, makeRequestCacheWithParent } from './request-cache';
|
|
3
|
+
export const getStepPrefetch = (httpClient, flowRequestCache, submissionBehaviors) => {
|
|
4
|
+
const requestCache = makeRequestCacheWithParent(flowRequestCache);
|
|
5
|
+
// keep track of all prefetched actions in this step so duplicate requests
|
|
6
|
+
// aren't made
|
|
7
|
+
const keys = new Set();
|
|
8
|
+
const start = (model) => {
|
|
9
|
+
if (keys.size > 0) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
submissionBehaviors.forEach((behavior) => {
|
|
13
|
+
const request = behavior.type === 'action'
|
|
14
|
+
? createRequestFromAction(behavior.action, model)
|
|
15
|
+
: behavior.launchConfig.request;
|
|
16
|
+
const requestParams = [
|
|
17
|
+
request.url,
|
|
18
|
+
{
|
|
19
|
+
body: JSON.stringify(request.body),
|
|
20
|
+
method: request.method,
|
|
21
|
+
headers: { 'Content-Type': 'application/json' },
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
try {
|
|
25
|
+
const key = makeRequestCacheKey(requestParams);
|
|
26
|
+
if (keys.has(key)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const responsePromise = httpClient(...requestParams).catch(() => null);
|
|
30
|
+
requestCache.set(requestParams, responsePromise);
|
|
31
|
+
// eslint-disable-next-line functional/immutable-data
|
|
32
|
+
keys.add(key);
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
// if fetch fails synchronously, we do not cache the request
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const stop = () => {
|
|
40
|
+
// no-op for now
|
|
41
|
+
};
|
|
42
|
+
return { requestCache, start, stop };
|
|
43
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* we can disable this rule here because a cache is inherently mutable */
|
|
2
|
+
/* eslint-disable functional/immutable-data */
|
|
3
|
+
export const makeRequestCacheWithParent = (parent) => {
|
|
4
|
+
const map = new Map();
|
|
5
|
+
const cache = {
|
|
6
|
+
get: (requestParams) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const key = makeRequestCacheKey(requestParams);
|
|
9
|
+
const promise = (_a = map.get(key)) !== null && _a !== void 0 ? _a : parent === null || parent === void 0 ? void 0 : parent.get(requestParams);
|
|
10
|
+
map.delete(key);
|
|
11
|
+
return promise;
|
|
12
|
+
},
|
|
13
|
+
set: (requestParams, responsePromise) => {
|
|
14
|
+
return map.set(makeRequestCacheKey(requestParams), responsePromise);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
return cache;
|
|
18
|
+
};
|
|
19
|
+
export const makeRequestCache = (initialValues = []) => {
|
|
20
|
+
const cache = makeRequestCacheWithParent(undefined);
|
|
21
|
+
initialValues.forEach(([requestParams, responsePromise]) => {
|
|
22
|
+
cache.set(requestParams, responsePromise);
|
|
23
|
+
});
|
|
24
|
+
return cache;
|
|
25
|
+
};
|
|
26
|
+
export const normaliseRequestCache = (cache) => {
|
|
27
|
+
if (cache === undefined) {
|
|
28
|
+
return makeRequestCache();
|
|
29
|
+
}
|
|
30
|
+
if (isRequestCacheInstance(cache)) {
|
|
31
|
+
return cache;
|
|
32
|
+
}
|
|
33
|
+
return makeRequestCache(cache);
|
|
34
|
+
};
|
|
35
|
+
const isRequestCacheInstance = (cache) => {
|
|
36
|
+
return !cache || !Array.isArray(cache);
|
|
37
|
+
};
|
|
38
|
+
export const makeRequestCacheKey = (requestParams) => {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
const [input, init] = requestParams;
|
|
41
|
+
const url = typeof input === 'string' || input instanceof URL ? input.toString() : input.url;
|
|
42
|
+
const key = JSON.stringify({
|
|
43
|
+
url,
|
|
44
|
+
method: (_a = init === null || init === void 0 ? void 0 : init.method) !== null && _a !== void 0 ? _a : 'GET',
|
|
45
|
+
headers: (init === null || init === void 0 ? void 0 : init.headers) ? Array.from(new Headers(init.headers).entries()) : [],
|
|
46
|
+
body: (_b = init === null || init === void 0 ? void 0 : init.body) !== null && _b !== void 0 ? _b : null,
|
|
47
|
+
});
|
|
48
|
+
return key;
|
|
49
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { makeRequestCache, makeRequestCacheWithParent } from './request-cache';
|
|
2
|
+
describe('RequestCache', () => {
|
|
3
|
+
it('should.set and.get requests correctly, discriminating by URL and method', async () => {
|
|
4
|
+
var _a, _b, _c, _d;
|
|
5
|
+
const cache = makeRequestCache();
|
|
6
|
+
cache.set(['/request-a', { method: 'GET' }], Promise.resolve(new Response('cached response: a GET')));
|
|
7
|
+
cache.set(['/request-a', { method: 'POST' }], Promise.resolve(new Response('cached response: a POST')));
|
|
8
|
+
cache.set(['/request-b', { method: 'GET' }], Promise.resolve(new Response('cached response: b GET')));
|
|
9
|
+
cache.set(['/request-b', { method: 'POST' }], Promise.resolve(new Response('cached response: b POST')));
|
|
10
|
+
const promiseAget = cache.get(['/request-a', { method: 'GET' }]);
|
|
11
|
+
expect(promiseAget).toBeDefined();
|
|
12
|
+
expect(promiseAget && (await ((_a = (await promiseAget)) === null || _a === void 0 ? void 0 : _a.text()))).toBe('cached response: a GET');
|
|
13
|
+
const nonCachedPromise = cache.get(['/request-c', { method: 'GET' }]);
|
|
14
|
+
expect(nonCachedPromise).toBeUndefined();
|
|
15
|
+
const promiseApost = cache.get(['/request-a', { method: 'POST' }]);
|
|
16
|
+
expect(promiseApost).toBeDefined();
|
|
17
|
+
expect(promiseApost && (await ((_b = (await promiseApost)) === null || _b === void 0 ? void 0 : _b.text()))).toBe('cached response: a POST');
|
|
18
|
+
const promiseBget = cache.get(['/request-b', { method: 'GET' }]);
|
|
19
|
+
expect(promiseBget).toBeDefined();
|
|
20
|
+
expect(promiseBget && (await ((_c = (await promiseBget)) === null || _c === void 0 ? void 0 : _c.text()))).toBe('cached response: b GET');
|
|
21
|
+
const promiseBpost = cache.get(['/request-b', { method: 'POST' }]);
|
|
22
|
+
expect(promiseBpost).toBeDefined();
|
|
23
|
+
expect(promiseBpost && (await ((_d = (await promiseBpost)) === null || _d === void 0 ? void 0 : _d.text()))).toBe('cached response: b POST');
|
|
24
|
+
});
|
|
25
|
+
it('should also discriminate when headers are different', async () => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
const cache = makeRequestCache();
|
|
28
|
+
cache.set(['/request-a', { headers: { 'X-Custom-Header': 'Value1' } }], Promise.resolve(new Response('cached response: header Value1')));
|
|
29
|
+
cache.set(['/request-a', { headers: { 'X-Custom-Header': 'Value2' } }], Promise.resolve(new Response('cached response: header Value2')));
|
|
30
|
+
const promiseHeader1 = cache.get(['/request-a', { headers: { 'X-Custom-Header': 'Value1' } }]);
|
|
31
|
+
expect(promiseHeader1).toBeDefined();
|
|
32
|
+
expect(promiseHeader1 && (await ((_a = (await promiseHeader1)) === null || _a === void 0 ? void 0 : _a.text()))).toBe('cached response: header Value1');
|
|
33
|
+
const promiseHeader2 = cache.get(['/request-a', { headers: { 'X-Custom-Header': 'Value2' } }]);
|
|
34
|
+
expect(promiseHeader2).toBeDefined();
|
|
35
|
+
expect(promiseHeader2 && (await ((_b = (await promiseHeader2)) === null || _b === void 0 ? void 0 : _b.text()))).toBe('cached response: header Value2');
|
|
36
|
+
});
|
|
37
|
+
it('should also discriminate when bodies are different', async () => {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const cache = makeRequestCache();
|
|
40
|
+
cache.set(['/request-a', { method: 'POST', body: 'body1' }], Promise.resolve(new Response('cached response: body1')));
|
|
41
|
+
cache.set(['/request-a', { method: 'POST', body: 'body2' }], Promise.resolve(new Response('cached response: body2')));
|
|
42
|
+
const promiseBody1 = cache.get(['/request-a', { method: 'POST', body: 'body1' }]);
|
|
43
|
+
expect(promiseBody1).toBeDefined();
|
|
44
|
+
expect(promiseBody1 && (await ((_a = (await promiseBody1)) === null || _a === void 0 ? void 0 : _a.text()))).toBe('cached response: body1');
|
|
45
|
+
const promiseBody2 = cache.get(['/request-a', { method: 'POST', body: 'body2' }]);
|
|
46
|
+
expect(promiseBody2).toBeDefined();
|
|
47
|
+
expect(promiseBody2 && (await ((_b = (await promiseBody2)) === null || _b === void 0 ? void 0 : _b.text()))).toBe('cached response: body2');
|
|
48
|
+
});
|
|
49
|
+
describe('when given a parent cache', () => {
|
|
50
|
+
it('gets from the child cache first if present', async () => {
|
|
51
|
+
var _a;
|
|
52
|
+
const parent = makeRequestCache();
|
|
53
|
+
parent.set(['/abc', { method: 'POST', body: 'abc' }], Promise.resolve(new Response('cached response: abc')));
|
|
54
|
+
const child = makeRequestCacheWithParent(parent);
|
|
55
|
+
child.set(['/abc', { method: 'POST', body: 'abc' }], Promise.resolve(new Response('cached response: def')));
|
|
56
|
+
const promiseBody = child.get(['/abc', { method: 'POST', body: 'abc' }]);
|
|
57
|
+
expect(promiseBody).toBeDefined();
|
|
58
|
+
expect(promiseBody && (await ((_a = (await promiseBody)) === null || _a === void 0 ? void 0 : _a.text()))).toBe('cached response: def');
|
|
59
|
+
});
|
|
60
|
+
it('gets from the parent cache if not present in the child', async () => {
|
|
61
|
+
var _a;
|
|
62
|
+
const parent = makeRequestCache();
|
|
63
|
+
parent.set(['/abc', { method: 'POST', body: 'abc' }], Promise.resolve(new Response('cached response: abc')));
|
|
64
|
+
const child = makeRequestCacheWithParent(parent);
|
|
65
|
+
const promiseBody1 = child.get(['/abc', { method: 'POST', body: 'abc' }]);
|
|
66
|
+
expect(promiseBody1).toBeDefined();
|
|
67
|
+
expect(promiseBody1 && (await ((_a = (await promiseBody1)) === null || _a === void 0 ? void 0 : _a.text()))).toBe('cached response: abc');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const ONE_SECOND = 1000;
|
|
2
|
+
export const getStepRefreshAfter = ({ refreshAfter, logEvent, onBehavior, }) => {
|
|
3
|
+
let timeout = null;
|
|
4
|
+
const targetTime = new Date(refreshAfter).getTime();
|
|
5
|
+
if (typeof refreshAfter !== 'string' || Number.isNaN(targetTime)) {
|
|
6
|
+
throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
|
|
7
|
+
}
|
|
8
|
+
const start = () => {
|
|
9
|
+
const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
|
|
10
|
+
timeout = setTimeout(() => {
|
|
11
|
+
void onBehavior({ type: 'refresh', analytics: { schema: 'refreshAfter' } });
|
|
12
|
+
}, timeLeft);
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
start,
|
|
16
|
+
stop: () => {
|
|
17
|
+
if (!timeout) {
|
|
18
|
+
logEvent('warning', 'Attempted to stop refreshAfter but it was not started');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
clearTimeout(timeout);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { getStepRefreshAfter } from './getStepRefreshAfter';
|
|
3
|
+
describe('getStepRefreshAfter', () => {
|
|
4
|
+
describe('valid ISO 8601 strings (and supported by JS Date)', () => {
|
|
5
|
+
const validIso8601Strings = [
|
|
6
|
+
['date, hours, minutes, seconds, no timezone', '2025-03-12T12:00:00'],
|
|
7
|
+
['date, hours, minutes, seconds, timezone Z', '2025-03-12T12:00:00Z'],
|
|
8
|
+
['date, hours, minutes, seconds, timezone +01:00', '2025-03-12T12:00:00+01:00'],
|
|
9
|
+
['date, hours, minutes, seconds, timezone -01:00', '2025-03-12T12:00:00-01:00'],
|
|
10
|
+
['date, hours, minutes, seconds.milliseconds, timezone Z', '2025-03-12T12:00:00.000Z'],
|
|
11
|
+
[
|
|
12
|
+
'date, hours, minutes, seconds.milliseconds, timezone +HH:mm',
|
|
13
|
+
'2025-03-12T12:00:00.000+01:00',
|
|
14
|
+
],
|
|
15
|
+
[
|
|
16
|
+
'date, hours, minutes, seconds.milliseconds, timezone +HHmm',
|
|
17
|
+
'2025-03-12T12:00:00.000+0100',
|
|
18
|
+
],
|
|
19
|
+
];
|
|
20
|
+
describe.each(validIso8601Strings)('%s', (_, refreshAfter) => {
|
|
21
|
+
it(`it returns a StepRefreshAfter object for "${refreshAfter}"`, () => {
|
|
22
|
+
expect(getStepRefreshAfter({ refreshAfter, logEvent: vi.fn(), onBehavior: vi.fn() })).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('invalid ISO 8601 strings (or not supported by JS Date)', () => {
|
|
27
|
+
const invalidIso8601Strings = [
|
|
28
|
+
['random word', 'banana'],
|
|
29
|
+
['random word', 'now'],
|
|
30
|
+
['unix timestamp', '1741780800'],
|
|
31
|
+
['basic date string: missing dashes and colons', '20250312T120000'],
|
|
32
|
+
['two-digit timezone +HH', '2025-03-12T12:00:00.000+01'],
|
|
33
|
+
];
|
|
34
|
+
describe.each(invalidIso8601Strings)('%s', (_, refreshAfter) => {
|
|
35
|
+
it(`it should throw for "${refreshAfter}"`, () => {
|
|
36
|
+
expect(() => getStepRefreshAfter({ refreshAfter, logEvent: vi.fn(), onBehavior: vi.fn() })).toThrow(`Invalid refreshAfter value: ${refreshAfter}`);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|