@lowdefy/server-dev 6.1.0 → 7.0.0
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/client/App.jsx +52 -24
- package/client/Inspector.jsx +73 -30
- package/client/JourneyObserver.jsx +47 -0
- package/client/Page.jsx +54 -22
- package/client/Recorder.jsx +73 -0
- package/client/Routing.jsx +37 -13
- package/client/clientBuildImports.test.mjs +2 -0
- package/client/devStreamConnections.test.mjs +40 -0
- package/client/feedback/FeedbackMount.jsx +2 -2
- package/client/feedback/FeedbackOverlay.jsx +7 -4
- package/client/feedback/feedbackStyles.js +3 -0
- package/client/journeyObserver/startJourneyObserver.js +101 -0
- package/client/journeyObserver/startJourneyObserver.test.mjs +149 -0
- package/client/openInEditor/OpenInEditorListener.jsx +2 -0
- package/client/recorder/buildEngineRecord.js +37 -0
- package/client/recorder/buildInteractionRecord.js +64 -0
- package/client/recorder/buildTraceEvent.js +57 -0
- package/client/recorder/buildTraceEvent.test.mjs +81 -0
- package/client/recorder/chunkRecords.js +69 -0
- package/client/recorder/chunkRecords.test.mjs +59 -0
- package/client/recorder/collectEventActions.js +30 -0
- package/client/recorder/collectEventEndpoints.js +40 -0
- package/client/recorder/collectEventRequests.js +39 -0
- package/client/recorder/createPairingBuffer.js +187 -0
- package/client/recorder/createPairingBuffer.test.mjs +405 -0
- package/client/recorder/createPasswordRedactor.js +84 -0
- package/client/recorder/createRecordBatcher.js +54 -0
- package/client/recorder/createRecordBatcher.test.mjs +76 -0
- package/client/recorder/createRecorder.js +216 -0
- package/client/recorder/createRecorder.test.mjs +318 -0
- package/client/recorder/createRecordingSession.js +61 -0
- package/client/recorder/createRecordingSession.test.mjs +72 -0
- package/client/recorder/diffStateWrites.js +77 -0
- package/client/recorder/diffStateWrites.test.mjs +66 -0
- package/client/recorder/isEventCall.js +30 -0
- package/client/recorder/jsonType.js +39 -0
- package/client/recorder/keyChord.js +47 -0
- package/client/recorder/keyChord.test.mjs +50 -0
- package/client/recorder/sendRecords.js +40 -0
- package/client/recorder/toJsonSafe.js +29 -0
- package/lib/client/BuildingPage.jsx +3 -1
- package/lib/client/ErrorBar.jsx +17 -13
- package/lib/client/RedirectingPage.jsx +56 -0
- package/lib/client/auth/Auth.jsx +11 -6
- package/lib/client/auth/AuthConfigured.jsx +222 -36
- package/lib/client/auth/AuthNotConfigured.js +45 -4
- package/lib/client/auth/authNotConfiguredParity.test.mjs +61 -0
- package/lib/client/auth/authTemplateSync.test.mjs +40 -0
- package/lib/client/auth/betterAuthSessionContract.test.mjs +181 -0
- package/lib/client/auth/createFetchCheckingBuild.js +45 -0
- package/lib/client/bundleBuildId.js +25 -0
- package/lib/client/collectVisibleBlockIds.js +51 -0
- package/lib/client/getCspNonce.js +25 -0
- package/lib/client/getPagePath.js +36 -0
- package/lib/client/getPagePath.test.mjs +51 -0
- package/lib/client/utils/formatErrorsForCopy.js +30 -0
- package/lib/client/utils/formatErrorsForCopy.test.mjs +39 -0
- package/lib/client/utils/getErrorBarColor.js +25 -0
- package/lib/client/utils/getErrorBarColor.test.mjs +34 -0
- package/lib/client/utils/request.js +8 -1
- package/lib/client/utils/usePageConfig.js +89 -25
- package/lib/client/utils/usePageConfig.test.mjs +198 -13
- package/lib/configTree/buildConfigTree.mjs +58 -0
- package/lib/configTree/buildConfigTree.test.mjs +77 -0
- package/lib/configTree/collectConfigText.mjs +101 -0
- package/lib/configTree/collectConfigText.test.mjs +116 -0
- package/lib/configTree/prepareScratchServer.mjs +46 -0
- package/lib/configTree/runConfigTreeBuild.mjs +88 -0
- package/lib/configTree/runConfigTreeBuild.skipPluginChecks.test.mjs +74 -0
- package/lib/configTree/runConfigTreeBuild.test.mjs +202 -0
- package/lib/docs/JourneyStepError.js +30 -0
- package/lib/docs/acquireBrowserSlot.js +30 -0
- package/lib/docs/addNestedEndpoints.js +54 -0
- package/lib/docs/addNestedEndpoints.test.mjs +185 -0
- package/lib/docs/addWriteFlags.js +77 -0
- package/lib/docs/buildEditedPages.js +50 -0
- package/lib/docs/buildWaitHeader.js +21 -0
- package/lib/docs/captureAnnotatedScreenshot.js +23 -12
- package/lib/docs/checkAgentFiles.js +148 -0
- package/lib/docs/checkAgentFiles.test.mjs +225 -0
- package/lib/docs/checkStepReferences.js +239 -0
- package/lib/docs/checkStepReferences.test.mjs +173 -0
- package/lib/docs/checkpointStore.js +3 -0
- package/lib/docs/clientErrorStore.js +18 -1
- package/lib/docs/clientErrorStore.test.mjs +46 -0
- package/lib/docs/collectExercised.js +52 -0
- package/lib/docs/createBrowserLifecycle.js +102 -0
- package/lib/docs/createBrowserLifecycle.test.mjs +212 -0
- package/lib/docs/createDocsMcpServer.js +345 -388
- package/lib/docs/createDocsMcpServer.test.mjs +644 -4
- package/lib/docs/createDocsMcpServerRunJourney.test.mjs +148 -0
- package/lib/docs/createJourneyActors.chromium.test.mjs +227 -0
- package/lib/docs/createJourneyActors.js +213 -0
- package/lib/docs/createJourneyActors.test.mjs +79 -0
- package/lib/docs/createJourneyObservations.js +80 -0
- package/lib/docs/createLeftOriginError.js +26 -0
- package/lib/docs/createNetworkCounter.js +101 -0
- package/lib/docs/dataSets/checkSharedCollectionIds.js +67 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +679 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
- package/lib/docs/dataSets/deserializeFixture.js +34 -0
- package/lib/docs/dataSets/drainSessionWork.js +37 -0
- package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
- package/lib/docs/dataSets/duplicateIndexName.js +24 -0
- package/lib/docs/dataSets/findCollectionCollisions.js +52 -0
- package/lib/docs/dataSets/getDataStore.js +106 -0
- package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
- package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
- package/lib/docs/dataSets/groupDataSetByCollection.js +44 -0
- package/lib/docs/dataSets/insertFixture.js +57 -0
- package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
- package/lib/docs/dataSets/isDataStoreGone.js +29 -0
- package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
- package/lib/docs/dataSets/openDataSession.js +119 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +343 -0
- package/lib/docs/dataSets/pickDataStorePort.js +58 -0
- package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
- package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
- package/lib/docs/dataSets/readDataSession.js +41 -0
- package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
- package/lib/docs/dataSets/readDataSet.js +68 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +220 -0
- package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
- package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
- package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
- package/lib/docs/dataSets/sweepOrphanDatabases.js +33 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +203 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +8 -0
- package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
- package/lib/docs/devEventBus.js +136 -0
- package/lib/docs/devEventBus.test.mjs +269 -0
- package/lib/docs/devToolDefinitions.js +529 -0
- package/lib/docs/docSources.js +21 -0
- package/lib/docs/docs.test.mjs +171 -15
- package/lib/docs/docsIndex.test.mjs +377 -0
- package/lib/docs/evalOperator.js +5 -3
- package/lib/docs/evalOperatorHeadless.js +84 -12
- package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
- package/lib/docs/evalOperatorInTab.js +2 -1
- package/lib/docs/findConfig.js +95 -49
- package/lib/docs/fitResponse.js +81 -0
- package/lib/docs/fitResponse.test.mjs +128 -0
- package/lib/docs/formatFeedback.js +26 -5
- package/lib/docs/formatFeedback.test.mjs +48 -0
- package/lib/docs/getAppMap.js +7 -1
- package/lib/docs/getBrowser.js +150 -80
- package/lib/docs/getBrowser.test.mjs +407 -16
- package/lib/docs/getBuildId.js +61 -0
- package/lib/docs/getBuildId.test.mjs +84 -0
- package/lib/docs/getBuildStatus.js +54 -6
- package/lib/docs/getBuildStatus.test.mjs +133 -0
- package/lib/docs/getBuildStatusAfterEdits.js +35 -0
- package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
- package/lib/docs/getCoreDoc.js +9 -1
- package/lib/docs/getDoc.js +100 -0
- package/lib/docs/getDocsIndex.js +152 -0
- package/lib/docs/getHazards.js +115 -0
- package/lib/docs/getHazards.test.mjs +175 -0
- package/lib/docs/getMailOutboxDirectory.js +26 -0
- package/lib/docs/getOverview.js +79 -13
- package/lib/docs/getOverviewBasePath.test.mjs +37 -0
- package/lib/docs/getPageBuildStatus.js +46 -0
- package/lib/docs/getPluginDoc.js +21 -23
- package/lib/docs/getSchema.js +33 -1
- package/lib/docs/getStaleStatus.js +36 -0
- package/lib/docs/getStaleStatus.test.mjs +74 -0
- package/lib/docs/guardJourneyOrigin.js +39 -0
- package/lib/docs/inspectState.js +5 -3
- package/lib/docs/inspectStateFromTab.js +2 -2
- package/lib/docs/inspectStateHeadless.js +89 -27
- package/lib/docs/inspectStateHeadless.test.mjs +20 -1
- package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
- package/lib/docs/installHeadlessShell.js +123 -0
- package/lib/docs/installHeadlessShell.test.mjs +70 -0
- package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
- package/lib/docs/isHeadlessShellIncomplete.js +34 -0
- package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
- package/lib/docs/isPageReady.js +77 -0
- package/lib/docs/isPageReady.test.mjs +235 -0
- package/lib/docs/isRunErrorEntry.js +25 -0
- package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
- package/lib/docs/journeyRunRecording.js +35 -0
- package/lib/docs/journeyRunRecording.test.mjs +39 -0
- package/lib/docs/keepEventStreamAlive.js +68 -0
- package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
- package/lib/docs/launchBrowser.js +83 -0
- package/lib/docs/launchBrowser.test.mjs +175 -0
- package/lib/docs/listModuleDocEntries.js +69 -0
- package/lib/docs/listPackageDocFiles.js +67 -0
- package/lib/docs/listPluginDocEntries.js +111 -0
- package/lib/docs/listPlugins.js +9 -58
- package/lib/docs/listTypes.js +14 -6
- package/lib/docs/loadFrameworkHazards.js +40 -0
- package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
- package/lib/docs/loadState.js +103 -35
- package/lib/docs/makeModuleManifestDoc.js +174 -0
- package/lib/docs/matchExercisedPages.js +35 -0
- package/lib/docs/matchJourneyRoute.js +69 -0
- package/lib/docs/matchJourneyRoute.test.mjs +178 -0
- package/lib/docs/mergeNetworkSnapshots.js +49 -0
- package/lib/docs/nextJourneySequence.js +30 -0
- package/lib/docs/noBrowserError.js +22 -0
- package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
- package/lib/docs/observe/claimExpectedErrors.js +67 -0
- package/lib/docs/observe/createJourneyAppErrors.js +236 -0
- package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
- package/lib/docs/observe/evaluateInvariants.js +183 -0
- package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
- package/lib/docs/observe/findingKey.js +29 -0
- package/lib/docs/observe/hasNoEffect.js +31 -0
- package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
- package/lib/docs/observe/installStepObserver.js +72 -0
- package/lib/docs/observe/isAppApiUrl.js +33 -0
- package/lib/docs/observe/parseAppApiUrl.js +37 -0
- package/lib/docs/observe/readStepObserver.js +42 -0
- package/lib/docs/observe/relativeSource.js +29 -0
- package/lib/docs/observe/resolveConfigKeySource.js +33 -0
- package/lib/docs/observe/takeJourneyEvents.js +36 -0
- package/lib/docs/observe/toAppErrorFailure.js +61 -0
- package/lib/docs/observe/usesSearchStage.js +45 -0
- package/lib/docs/observe/waitForClientErrorReports.js +31 -0
- package/lib/docs/observe/watchJourneyContext.js +75 -0
- package/lib/docs/observe/withAppErrors.js +31 -0
- package/lib/docs/observe/withAppErrors.test.mjs +101 -0
- package/lib/docs/openJourney.js +94 -0
- package/lib/docs/openJourneyEmail.js +41 -0
- package/lib/docs/openJourneyEmail.test.mjs +187 -0
- package/lib/docs/pageBlocks.js +47 -0
- package/lib/docs/pageBuildStatus.test.mjs +103 -0
- package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
- package/lib/docs/rails/authEngineActions.js +53 -0
- package/lib/docs/rails/authEngineActions.test.mjs +69 -0
- package/lib/docs/rails/authEngineMethods.js +52 -0
- package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
- package/lib/docs/rails/checkAuthActionsRan.js +66 -0
- package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
- package/lib/docs/rails/checkClickRails.js +56 -0
- package/lib/docs/rails/createDataSetRails.js +82 -0
- package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
- package/lib/docs/rails/findExternalBlocks.js +147 -0
- package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
- package/lib/docs/rails/installAuthActionRecorder.js +56 -0
- package/lib/docs/rails/readAuthActionRecorder.js +32 -0
- package/lib/docs/rails/readBuiltArtifact.js +30 -0
- package/lib/docs/readConnectionPlugins.js +26 -0
- package/lib/docs/readDevAuthMode.js +32 -0
- package/lib/docs/readDocEntry.js +40 -0
- package/lib/docs/readDocTitle.js +26 -0
- package/lib/docs/readJourneyEmailMatch.js +57 -0
- package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
- package/lib/docs/readJourneySession.js +56 -0
- package/lib/docs/readMailOutbox.js +37 -0
- package/lib/docs/readPagePath.js +27 -0
- package/lib/docs/readPluginPackages.js +99 -0
- package/lib/docs/readProxyBuildWait.js +38 -0
- package/lib/docs/readRoutineSteps.js +45 -0
- package/lib/docs/registerShownTextEngine.js +118 -0
- package/lib/docs/requestRestart.js +30 -0
- package/lib/docs/requestRestart.test.mjs +49 -0
- package/lib/docs/resolvePageInstance.js +46 -0
- package/lib/docs/resolvePageInstance.test.mjs +81 -0
- package/lib/docs/resolveSource.js +11 -7
- package/lib/docs/resolveSource.test.mjs +1 -1
- package/lib/docs/resolveToolCaller.js +62 -0
- package/lib/docs/reviewPage.js +51 -0
- package/lib/docs/reviewPageBuilds.js +56 -0
- package/lib/docs/reviewPageBuilds.test.mjs +207 -0
- package/lib/docs/runCheck.js +75 -0
- package/lib/docs/runCheck.test.mjs +91 -0
- package/lib/docs/runCheckChild.js +49 -0
- package/lib/docs/runEndpoint.js +185 -0
- package/lib/docs/runEndpoint.test.mjs +509 -0
- package/lib/docs/runErrorBuffers.js +99 -0
- package/lib/docs/runErrorBuffers.test.mjs +95 -0
- package/lib/docs/runJourney.js +347 -0
- package/lib/docs/runJourney.test.mjs +2383 -0
- package/lib/docs/runJourneyAsUsers.js +44 -0
- package/lib/docs/runJourneyDataSet.test.mjs +274 -0
- package/lib/docs/runJourneySteps.js +1320 -0
- package/lib/docs/runRequest.js +100 -59
- package/lib/docs/runRequest.test.mjs +190 -0
- package/lib/docs/saveAnnotatedScreenshot.js +1 -1
- package/lib/docs/scaffoldPage.js +3 -1
- package/lib/docs/screenshotPage.js +163 -11
- package/lib/docs/screenshotPage.test.mjs +263 -1
- package/lib/docs/screenshotPageClip.test.mjs +4 -0
- package/lib/docs/searchDocs.js +82 -30
- package/lib/docs/searchDocs.test.mjs +100 -0
- package/lib/docs/searchIcons.js +90 -0
- package/lib/docs/searchIcons.test.mjs +127 -0
- package/lib/docs/selectExercised.js +39 -0
- package/lib/docs/selectFinalState.js +66 -0
- package/lib/docs/selectFinalState.test.mjs +51 -0
- package/lib/docs/serverErrorStore.js +54 -0
- package/lib/docs/serverErrorStore.test.mjs +50 -0
- package/lib/docs/setupTestFixtures.mjs +133 -39
- package/lib/docs/shownTextSelector.js +28 -0
- package/lib/docs/snapshotState.js +13 -5
- package/lib/docs/splitSearchTerms.js +62 -0
- package/lib/docs/splitSearchTerms.test.mjs +50 -0
- package/lib/docs/stateCheckpoints.test.mjs +77 -0
- package/lib/docs/tabAvailable.js +6 -5
- package/lib/docs/tabChannel.js +127 -21
- package/lib/docs/tabChannel.test.mjs +187 -29
- package/lib/docs/unsettledPageNote.js +24 -0
- package/lib/docs/validateJourneyMail.js +36 -0
- package/lib/docs/validateJourneyTimeout.js +38 -0
- package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
- package/lib/docs/validateRecordingParam.js +42 -0
- package/lib/docs/validateStateSelection.js +33 -0
- package/lib/docs/validateStateSelection.test.mjs +33 -0
- package/lib/docs/validateViewport.js +51 -0
- package/lib/docs/validateViewport.test.mjs +54 -0
- package/lib/docs/waitForBuild.js +50 -0
- package/lib/docs/waitForBuild.test.mjs +75 -0
- package/lib/docs/waitForJourneyEmail.js +77 -0
- package/lib/docs/withBrowserSlot.js +45 -0
- package/lib/docs/withBrowserSlot.test.mjs +75 -0
- package/lib/docs/withDataSession.js +53 -0
- package/lib/server/applyDataSetRedirect.js +83 -0
- package/lib/server/applyDataSetRedirect.test.mjs +340 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +204 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +131 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/{getAuthConfig.js → getAuth.js} +15 -9
- package/lib/server/auth/getHeadlessUser.js +9 -8
- package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
- package/lib/server/auth/getMockUser.js +18 -14
- package/lib/server/auth/{session.js → getStrategies.js} +17 -16
- package/lib/server/auth/headlessUser.js +3 -2
- package/lib/server/auth/journeyActor.js +35 -0
- package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
- package/lib/server/checkPageRecord.js +44 -0
- package/lib/server/contextMapBudget.js +24 -0
- package/lib/server/contextMapBudget.test.mjs +98 -0
- package/lib/server/continueJitKeys.js +28 -0
- package/lib/server/continueJitKeys.test.mjs +43 -0
- package/lib/server/createDataSessionEndedError.js +33 -0
- package/lib/server/createDataSetRefusal.js +26 -0
- package/lib/server/createLowdefyContext.js +168 -41
- package/lib/server/createLowdefyContext.test.mjs +328 -8
- package/lib/server/getClientAddress.js +51 -0
- package/lib/server/getClientAddress.test.mjs +56 -0
- package/lib/server/getPageJitEnrichment.test.mjs +29 -11
- package/lib/server/hashConfigContent.js +26 -0
- package/lib/server/importAppCode.test.mjs +57 -0
- package/lib/server/isRoutineStep.js +27 -0
- package/lib/server/jitPageBuilder.js +309 -126
- package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
- package/lib/server/jitPageBuilder.test.mjs +218 -0
- package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
- package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
- package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
- package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
- package/lib/server/journeyCookies.js +22 -0
- package/lib/server/journeyCookies.test.mjs +145 -0
- package/lib/server/loadDynamicJsMap.js +47 -0
- package/lib/server/log/createHandleError.js +38 -2
- package/lib/server/log/createHandleError.test.mjs +125 -2
- package/lib/server/log/createLogger.js +3 -0
- package/lib/server/log/createLogger.test.mjs +15 -0
- package/lib/server/log/logRequest.js +2 -3
- package/lib/server/mutantDataSetWrappers.test.mjs +112 -0
- package/lib/server/mutants/applyMutant.js +39 -0
- package/lib/server/mutants/artifactPatterns.js +25 -0
- package/lib/server/mutants/buildAnchor.js +45 -0
- package/lib/server/mutants/collectSharedIds.js +41 -0
- package/lib/server/mutants/configPathSegments.js +38 -0
- package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
- package/lib/server/mutants/describeEvent.js +22 -0
- package/lib/server/mutants/enumerateArtifact.js +56 -0
- package/lib/server/mutants/findNodeByKey.js +45 -0
- package/lib/server/mutants/findNodeByPointer.js +49 -0
- package/lib/server/mutants/getArtifactKind.js +32 -0
- package/lib/server/mutants/groupMutantCopies.js +56 -0
- package/lib/server/mutants/hashArtifact.js +25 -0
- package/lib/server/mutants/hashMutantTarget.js +34 -0
- package/lib/server/mutants/listMutants.js +122 -0
- package/lib/server/mutants/listMutants.test.mjs +502 -0
- package/lib/server/mutants/mutantId.js +30 -0
- package/lib/server/mutants/mutantRuns.js +57 -0
- package/lib/server/mutants/mutantRuns.test.mjs +136 -0
- package/lib/server/mutants/operators/dropAction.js +47 -0
- package/lib/server/mutants/operators/dropBlock.js +44 -0
- package/lib/server/mutants/operators/dropPayload.js +52 -0
- package/lib/server/mutants/operators/dropStep.js +44 -0
- package/lib/server/mutants/operators/flipVisible.js +53 -0
- package/lib/server/mutants/operators/index.js +37 -0
- package/lib/server/mutants/operators/retargetLink.js +72 -0
- package/lib/server/mutants/operators/skipValidate.js +77 -0
- package/lib/server/mutants/operators/swapIf.js +112 -0
- package/lib/server/mutants/operators.test.mjs +396 -0
- package/lib/server/mutants/positionFreePath.js +38 -0
- package/lib/server/mutants/removeFromParent.js +28 -0
- package/lib/server/mutants/test/fixtures.mjs +173 -0
- package/lib/server/mutants/test/keyArtifact.mjs +63 -0
- package/lib/server/mutants/validateMutantParam.js +69 -0
- package/lib/server/mutants/validateMutantsBody.js +88 -0
- package/lib/server/mutants/walkArtifact.js +179 -0
- package/lib/server/pageBuildRecords.js +131 -0
- package/lib/server/pageBuildRecords.test.mjs +258 -0
- package/lib/server/pageCache.mjs +22 -7
- package/lib/server/pageCache.test.mjs +22 -7
- package/lib/server/pageRebuilds.test.mjs +543 -0
- package/lib/server/pruneJitMaps.js +36 -0
- package/lib/server/pruneJitMaps.test.mjs +55 -0
- package/lib/server/readInstanceRecord.js +33 -0
- package/lib/server/readInstanceRecord.test.mjs +99 -0
- package/lib/server/readMergedMaps.js +86 -0
- package/lib/server/readMergedMaps.test.mjs +79 -0
- package/lib/server/recording/appendRecords.js +33 -0
- package/lib/server/recording/errorRecordingStamp.js +34 -0
- package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
- package/lib/server/recording/isRecordingEnabled.js +31 -0
- package/lib/server/recording/readRecordingCookie.js +71 -0
- package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
- package/lib/server/recording/recordingCookiePayload.js +33 -0
- package/lib/server/recording/servedBuilds.js +46 -0
- package/lib/server/recording/servedBuilds.test.mjs +42 -0
- package/lib/server/sinkExternalConnections.js +95 -0
- package/lib/server/sinkExternalConnections.test.mjs +135 -0
- package/lib/server/skipStaleMapWrites.js +62 -0
- package/lib/server/skipStaleMapWrites.test.mjs +113 -0
- package/lib/server/startIdleGc.js +67 -0
- package/lib/server/startIdleGc.test.mjs +138 -0
- package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
- package/lib/server/trackSessionWork.js +30 -0
- package/lib/vite/createDevServerExclude.js +40 -0
- package/lib/vite/createDevServerExclude.test.mjs +79 -0
- package/manager/getContext.mjs +24 -2
- package/manager/processes/initialBuild.mjs +14 -0
- package/manager/processes/initialBuild.test.mjs +101 -0
- package/manager/processes/lowdefyBuild.mjs +30 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +62 -0
- package/manager/processes/lowdefyBuild.test.mjs +136 -1
- package/manager/processes/optimizeDependencies.mjs +59 -0
- package/manager/processes/restartServer.mjs +34 -6
- package/manager/processes/restartServer.test.mjs +162 -0
- package/manager/processes/shutdownServer.mjs +11 -0
- package/manager/processes/shutdownServer.test.mjs +45 -0
- package/manager/processes/startFirstServer.mjs +46 -0
- package/manager/processes/startFirstServer.test.mjs +326 -0
- package/manager/processes/startMailSink.mjs +135 -0
- package/manager/processes/startMailSink.test.mjs +146 -0
- package/manager/processes/startProxy.mjs +129 -12
- package/manager/processes/startProxy.test.mjs +366 -0
- package/manager/processes/startServer.mjs +47 -6
- package/manager/processes/startServer.test.mjs +155 -0
- package/manager/processes/startWatchers.mjs +6 -4
- package/manager/processes/syncServer.mjs +69 -0
- package/manager/processes/syncServer.test.mjs +141 -0
- package/manager/processes/waitForDevServer.mjs +48 -0
- package/manager/processes/waitForDevServer.test.mjs +112 -0
- package/manager/run.mjs +128 -43
- package/manager/utils/BatchChanges.mjs +21 -5
- package/manager/utils/BatchChanges.test.mjs +77 -0
- package/manager/utils/acquireDevInstance.mjs +99 -0
- package/manager/utils/acquireDevInstance.test.mjs +160 -0
- package/manager/utils/createBuildActivity.mjs +67 -0
- package/manager/utils/createBuildActivity.test.mjs +58 -0
- package/manager/utils/createChangeSignal.mjs +39 -0
- package/manager/utils/createChangeSignal.test.mjs +62 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
- package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
- package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
- package/manager/utils/createDotPathIgnore.mjs +44 -0
- package/manager/utils/createDotPathIgnore.test.mjs +65 -0
- package/manager/utils/createIdleGc.mjs +51 -0
- package/manager/utils/createIdleGc.test.mjs +77 -0
- package/manager/utils/createRequestActivity.mjs +100 -0
- package/manager/utils/createRequestActivity.test.mjs +121 -0
- package/manager/utils/createServerArtifactTracker.mjs +83 -0
- package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
- package/manager/utils/createServerEnv.mjs +39 -0
- package/manager/utils/filesHaveSameBytes.mjs +41 -0
- package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
- package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
- package/manager/utils/importFresh.mjs +46 -0
- package/manager/utils/importFresh.test.mjs +72 -0
- package/manager/utils/importFreshWorker.mjs +20 -0
- package/manager/utils/killTaggedBrowser.mjs +35 -0
- package/manager/utils/pagePathChanged.mjs +56 -0
- package/manager/utils/pruneRecordings.mjs +85 -0
- package/manager/utils/pruneRecordings.test.mjs +117 -0
- package/manager/utils/publishBuildDirectory.mjs +121 -0
- package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
- package/manager/utils/publishBuildDirectory.test.mjs +262 -0
- package/manager/utils/readBasePath.mjs +27 -0
- package/manager/utils/readBasePath.test.mjs +43 -0
- package/manager/utils/readBuildStatusWait.mjs +67 -0
- package/manager/utils/readManagerVersion.mjs +30 -0
- package/manager/utils/readManagerVersion.test.mjs +35 -0
- package/manager/utils/readPluginDefinitions.mjs +36 -0
- package/manager/utils/resolveDevAuthUrl.mjs +58 -0
- package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
- package/manager/utils/resolvePorts.mjs +49 -0
- package/manager/utils/setupWatcher.mjs +19 -11
- package/manager/utils/startRecordingPruner.mjs +45 -0
- package/manager/utils/startRecordingPruner.test.mjs +66 -0
- package/manager/utils/waitForChildExit.mjs +53 -0
- package/manager/utils/waitForChildExit.test.mjs +70 -0
- package/manager/utils/waitForServer.mjs +44 -0
- package/manager/utils/writeBuildStatus.mjs +1 -1
- package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
- package/manager/watchers/envWatcher.mjs +9 -2
- package/manager/watchers/envWatcher.test.mjs +87 -0
- package/manager/watchers/listServerSidePackages.mjs +47 -0
- package/manager/watchers/listServerSidePackages.test.mjs +38 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
- package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
- package/manager/watchers/pluginSourceWatcher.mjs +79 -0
- package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
- package/manager/watchers/restartRequestWatcher.mjs +71 -0
- package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
- package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
- package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
- package/manager/watchers/serverPackageWatcher.mjs +38 -0
- package/package.json +60 -47
- package/package.original.json +60 -47
- package/src/app.js +131 -20
- package/src/html/renderDevPage.js +63 -11
- package/src/html/renderDevPage.test.mjs +138 -0
- package/src/lib/getRequestPath.js +29 -0
- package/src/middleware/createSameOriginGuard.js +37 -0
- package/src/middleware/createSameOriginGuard.test.mjs +102 -0
- package/src/middleware/errorHandler.js +51 -1
- package/src/middleware/errorHandler.test.mjs +173 -3
- package/src/middleware/isRebindingSafeHost.js +44 -0
- package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
- package/src/middleware/localDevToolsOnly.js +38 -0
- package/src/middleware/localDevToolsOnly.test.mjs +61 -0
- package/src/middleware/noStore.js +27 -0
- package/src/middleware/noStore.test.mjs +48 -0
- package/src/middleware/requestTimeout.js +38 -0
- package/src/middleware/staleFlag.js +76 -0
- package/src/middleware/staleFlag.test.mjs +127 -0
- package/src/middleware/stampCaller.js +38 -0
- package/src/middleware/stampCaller.test.mjs +64 -0
- package/src/routes/agent.js +15 -3
- package/src/routes/auth.js +29 -20
- package/src/routes/auth.test.mjs +30 -56
- package/src/routes/clientError.js +13 -13
- package/src/routes/cron.js +3 -1
- package/src/routes/cronForward.js +3 -1
- package/src/routes/detached.js +22 -9
- package/src/routes/devInspect.js +17 -8
- package/src/routes/devInspect.test.mjs +44 -7
- package/src/routes/devRecording.js +113 -0
- package/src/routes/devRecording.test.mjs +253 -0
- package/src/routes/docs/buildStatus.js +10 -2
- package/src/routes/docs/buildStatus.test.mjs +46 -0
- package/src/routes/docs/content.js +5 -3
- package/src/routes/docs/evalOperator.js +15 -1
- package/src/routes/docs/events.js +50 -0
- package/src/routes/docs/events.test.mjs +117 -0
- package/src/routes/docs/find.js +9 -1
- package/src/routes/docs/icons.js +30 -0
- package/src/routes/docs/inspectState.js +16 -1
- package/src/routes/docs/journey.js +194 -0
- package/src/routes/docs/journey.test.mjs +401 -0
- package/src/routes/docs/journeyRefusals.test.mjs +71 -0
- package/src/routes/docs/loadState.js +4 -2
- package/src/routes/docs/mcp.js +34 -3
- package/src/routes/docs/mcp.test.mjs +130 -0
- package/src/routes/docs/mutants.js +78 -0
- package/src/routes/docs/mutants.test.mjs +180 -0
- package/src/routes/docs/parsePathParamsParam.js +41 -0
- package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
- package/src/routes/docs/parseUserParam.js +27 -21
- package/src/routes/docs/parseUserParam.test.mjs +17 -5
- package/src/routes/docs/restart.js +35 -0
- package/src/routes/docs/restart.test.mjs +64 -0
- package/src/routes/docs/runEndpoint.js +54 -0
- package/src/routes/docs/runEndpoint.test.mjs +165 -0
- package/src/routes/docs/runRequest.js +25 -8
- package/src/routes/docs/runRequest.test.mjs +92 -0
- package/src/routes/docs/screenshot.js +50 -2
- package/src/routes/docs/screenshot.test.mjs +117 -0
- package/src/routes/docs/search.js +15 -2
- package/src/routes/docs/snapshotState.js +17 -3
- package/src/routes/endpoints.js +27 -26
- package/src/routes/endpointsTextAnswer.test.mjs +77 -0
- package/src/routes/feedback.js +10 -13
- package/src/routes/jitPage.js +99 -35
- package/src/routes/jitPage.test.mjs +248 -0
- package/src/routes/mcp.js +82 -5
- package/src/routes/mcp.test.mjs +215 -0
- package/src/routes/mcpMemberToken.test.mjs +125 -0
- package/src/routes/mcpProtectedResourceMetadata.js +34 -0
- package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
- package/src/routes/mountDevPageRoutes.js +25 -0
- package/src/routes/mountDevPageRoutes.test.mjs +30 -0
- package/src/routes/mountOauthDiscovery.js +44 -0
- package/src/routes/mountOauthDiscovery.test.mjs +79 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
- package/src/routes/reload.js +16 -0
- package/src/routes/request.js +6 -4
- package/src/routes/request.test.mjs +9 -5
- package/src/routes/requestBody.test.mjs +159 -0
- package/src/routes/usage.js +6 -2
- package/src/routes/user.js +27 -0
- package/src/routes/websocket.js +29 -8
- package/src/routes/websocket.test.mjs +65 -0
- package/src/routes/wellKnownFallback.js +30 -0
- package/src/routes/wellKnownFallback.test.mjs +32 -0
- package/src/websocket/devWebSocket.js +41 -26
- package/src/websocket/devWebSocket.test.mjs +157 -0
- package/vite.config.js +53 -23
- package/lib/server/auth/getDevSession.js +0 -70
- package/lib/server/auth/getDevSession.test.mjs +0 -138
- package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
- package/manager/watchers/moduleBuildWatcher.mjs +0 -79
- package/manager/watchers/serverArtifactWatcher.mjs +0 -120
- package/src/routes/apiPage.js +0 -37
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import build from '@lowdefy/build';
|
|
20
|
+
import defaultMessagesMap from '@lowdefy/build/defaultMessagesMap';
|
|
21
|
+
import { serializeBuildException } from '@lowdefy/build/dev';
|
|
22
|
+
import { mergeObjects } from '@lowdefy/helpers';
|
|
23
|
+
|
|
24
|
+
import collectConfigText from './collectConfigText.mjs';
|
|
25
|
+
import createCustomPluginMessagesMap from '../../manager/utils/createCustomPluginMessagesMap.mjs';
|
|
26
|
+
import createCustomPluginTypesMap from '../../manager/utils/createCustomPluginTypesMap.mjs';
|
|
27
|
+
import prepareScratchServer from './prepareScratchServer.mjs';
|
|
28
|
+
|
|
29
|
+
// A full config build of one tree for the journeys scope diff: the pipeline
|
|
30
|
+
// `lowdefy build` runs, without the Vite client build, at stage dev so a
|
|
31
|
+
// prodError warning stays a warning as it does in the dev server. The plugin
|
|
32
|
+
// types come from the installed dev server, so base and head are built by
|
|
33
|
+
// one builder and a Lowdefy version bump does not show as a config change.
|
|
34
|
+
// Writes <out>/build/, <out>/server/ and <out>/result.json, which the CLI
|
|
35
|
+
// reads for the build's errors and warnings. A build that succeeds also
|
|
36
|
+
// writes <out>/configText.json, the app's config text set, which the journey
|
|
37
|
+
// readers resolve production text tokens against.
|
|
38
|
+
async function runConfigTreeBuild({
|
|
39
|
+
configDirectory,
|
|
40
|
+
outDirectory,
|
|
41
|
+
devDirectory,
|
|
42
|
+
logger,
|
|
43
|
+
refResolver,
|
|
44
|
+
skipPluginChecks,
|
|
45
|
+
}) {
|
|
46
|
+
const directories = {
|
|
47
|
+
config: configDirectory,
|
|
48
|
+
build: path.join(outDirectory, 'build'),
|
|
49
|
+
server: path.join(outDirectory, 'server'),
|
|
50
|
+
};
|
|
51
|
+
const resultPath = path.join(outDirectory, 'result.json');
|
|
52
|
+
const configTextPath = path.join(outDirectory, 'configText.json');
|
|
53
|
+
await fs.promises.rm(resultPath, { force: true });
|
|
54
|
+
await fs.promises.rm(configTextPath, { force: true });
|
|
55
|
+
await prepareScratchServer({ devDirectory, serverDirectory: directories.server });
|
|
56
|
+
let result;
|
|
57
|
+
try {
|
|
58
|
+
const [customTypesMap, customMessagesMap] = await Promise.all([
|
|
59
|
+
createCustomPluginTypesMap({ directories, logger }),
|
|
60
|
+
createCustomPluginMessagesMap({ directories, logger }),
|
|
61
|
+
]);
|
|
62
|
+
await build({
|
|
63
|
+
customMessagesMap,
|
|
64
|
+
customTypesMap,
|
|
65
|
+
directories,
|
|
66
|
+
logger,
|
|
67
|
+
refResolver,
|
|
68
|
+
skipPluginChecks,
|
|
69
|
+
stage: 'dev',
|
|
70
|
+
});
|
|
71
|
+
const configText = await collectConfigText({
|
|
72
|
+
buildDirectory: directories.build,
|
|
73
|
+
messagesMap: mergeObjects([defaultMessagesMap, customMessagesMap]),
|
|
74
|
+
});
|
|
75
|
+
await fs.promises.writeFile(configTextPath, JSON.stringify(configText, null, 2));
|
|
76
|
+
result = { status: 'ok', errors: [], warnings: [] };
|
|
77
|
+
} catch (error) {
|
|
78
|
+
result = {
|
|
79
|
+
status: 'error',
|
|
80
|
+
errors: error.errors ?? [serializeBuildException(error)],
|
|
81
|
+
warnings: error.warnings ?? [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
await fs.promises.writeFile(resultPath, JSON.stringify(result, null, 2));
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default runConfigTreeBuild;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { jest } from '@jest/globals';
|
|
21
|
+
|
|
22
|
+
const mockBuild = jest.fn();
|
|
23
|
+
|
|
24
|
+
jest.unstable_mockModule('@lowdefy/build', () => ({ default: mockBuild }));
|
|
25
|
+
jest.unstable_mockModule('./collectConfigText.mjs', () => ({
|
|
26
|
+
default: jest.fn().mockResolvedValue({}),
|
|
27
|
+
}));
|
|
28
|
+
jest.unstable_mockModule('./prepareScratchServer.mjs', () => ({
|
|
29
|
+
default: jest.fn().mockResolvedValue(),
|
|
30
|
+
}));
|
|
31
|
+
jest.unstable_mockModule('../../manager/utils/createCustomPluginTypesMap.mjs', () => ({
|
|
32
|
+
default: jest.fn().mockResolvedValue({}),
|
|
33
|
+
}));
|
|
34
|
+
jest.unstable_mockModule('../../manager/utils/createCustomPluginMessagesMap.mjs', () => ({
|
|
35
|
+
default: jest.fn().mockResolvedValue({}),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const { default: runConfigTreeBuild } = await import('./runConfigTreeBuild.mjs');
|
|
39
|
+
|
|
40
|
+
const logger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} };
|
|
41
|
+
let root;
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-config-tree-skip-'));
|
|
45
|
+
mockBuild.mockResolvedValue();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('runConfigTreeBuild passes skipPluginChecks true to build', async () => {
|
|
53
|
+
const result = await runConfigTreeBuild({
|
|
54
|
+
configDirectory: path.join(root, 'app'),
|
|
55
|
+
outDirectory: root,
|
|
56
|
+
devDirectory: path.join(root, 'dev'),
|
|
57
|
+
logger,
|
|
58
|
+
skipPluginChecks: true,
|
|
59
|
+
});
|
|
60
|
+
expect(result).toEqual({ status: 'ok', errors: [], warnings: [] });
|
|
61
|
+
const [args] = mockBuild.mock.calls[0];
|
|
62
|
+
expect(args.skipPluginChecks).toBe(true);
|
|
63
|
+
expect(args.stage).toEqual('dev');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('runConfigTreeBuild passes skipPluginChecks undefined to build when not given', async () => {
|
|
67
|
+
await runConfigTreeBuild({
|
|
68
|
+
configDirectory: path.join(root, 'app'),
|
|
69
|
+
outDirectory: root,
|
|
70
|
+
devDirectory: path.join(root, 'dev'),
|
|
71
|
+
logger,
|
|
72
|
+
});
|
|
73
|
+
expect(mockBuild.mock.calls[0][0].skipPluginChecks).toBeUndefined();
|
|
74
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import crypto from 'node:crypto';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { jest } from '@jest/globals';
|
|
23
|
+
|
|
24
|
+
import runConfigTreeBuild from './runConfigTreeBuild.mjs';
|
|
25
|
+
|
|
26
|
+
jest.setTimeout(120000);
|
|
27
|
+
|
|
28
|
+
const serverDevDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
29
|
+
|
|
30
|
+
const logger = {
|
|
31
|
+
debug: () => {},
|
|
32
|
+
info: () => {},
|
|
33
|
+
warn: () => {},
|
|
34
|
+
error: () => {},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function writeApp({ directory, title }) {
|
|
38
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
39
|
+
fs.writeFileSync(
|
|
40
|
+
path.join(directory, 'lowdefy.yaml'),
|
|
41
|
+
[
|
|
42
|
+
'lowdefy: 6.0.0',
|
|
43
|
+
'pages:',
|
|
44
|
+
' - id: home',
|
|
45
|
+
' type: Box',
|
|
46
|
+
' blocks:',
|
|
47
|
+
' - id: title',
|
|
48
|
+
' type: Html',
|
|
49
|
+
' properties:',
|
|
50
|
+
` html: ${title}`,
|
|
51
|
+
'',
|
|
52
|
+
].join('\n')
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// A stand-in for the app's .lowdefy/dev: the real server-dev package.json,
|
|
57
|
+
// public_default and node_modules, plus a public/ and a tailwind/ the running
|
|
58
|
+
// dev server would hold.
|
|
59
|
+
function writeDevDirectory({ directory }) {
|
|
60
|
+
fs.mkdirSync(path.join(directory, 'public'), { recursive: true });
|
|
61
|
+
fs.mkdirSync(path.join(directory, 'lowdefy-build', 'tailwind'), { recursive: true });
|
|
62
|
+
fs.copyFileSync(
|
|
63
|
+
path.join(serverDevDirectory, 'package.json'),
|
|
64
|
+
path.join(directory, 'package.json')
|
|
65
|
+
);
|
|
66
|
+
fs.cpSync(
|
|
67
|
+
path.join(serverDevDirectory, 'public_default'),
|
|
68
|
+
path.join(directory, 'public_default'),
|
|
69
|
+
{
|
|
70
|
+
recursive: true,
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
fs.writeFileSync(path.join(directory, 'public', 'logo.svg'), '<svg>live</svg>');
|
|
74
|
+
fs.writeFileSync(
|
|
75
|
+
path.join(directory, 'lowdefy-build', 'tailwind', 'pages.css'),
|
|
76
|
+
'@source "live";'
|
|
77
|
+
);
|
|
78
|
+
fs.symlinkSync(
|
|
79
|
+
path.join(serverDevDirectory, 'node_modules'),
|
|
80
|
+
path.join(directory, 'node_modules')
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function hashTree(directory) {
|
|
85
|
+
const hash = crypto.createHash('sha1');
|
|
86
|
+
function walk(current) {
|
|
87
|
+
fs.readdirSync(current, { withFileTypes: true })
|
|
88
|
+
.filter((entry) => entry.name !== 'node_modules')
|
|
89
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
90
|
+
.forEach((entry) => {
|
|
91
|
+
const full = path.join(current, entry.name);
|
|
92
|
+
hash.update(path.relative(directory, full));
|
|
93
|
+
if (entry.isDirectory()) {
|
|
94
|
+
walk(full);
|
|
95
|
+
} else {
|
|
96
|
+
hash.update(fs.readFileSync(full));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
walk(directory);
|
|
101
|
+
return hash.digest('hex');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let root;
|
|
105
|
+
beforeEach(() => {
|
|
106
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-config-tree-build-'));
|
|
107
|
+
});
|
|
108
|
+
afterEach(() => {
|
|
109
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('runConfigTreeBuild builds base and head into scratch directories and writes nothing under the dev directory', async () => {
|
|
113
|
+
const devDirectory = path.join(root, 'app', '.lowdefy', 'dev');
|
|
114
|
+
writeApp({ directory: path.join(root, 'app'), title: 'Head' });
|
|
115
|
+
writeApp({ directory: path.join(root, 'base-tree', 'app'), title: 'Base' });
|
|
116
|
+
writeDevDirectory({ directory: devDirectory });
|
|
117
|
+
const before = hashTree(devDirectory);
|
|
118
|
+
|
|
119
|
+
const [base, head] = await Promise.all([
|
|
120
|
+
runConfigTreeBuild({
|
|
121
|
+
configDirectory: path.join(root, 'base-tree', 'app'),
|
|
122
|
+
outDirectory: path.join(root, 'builds', 'base'),
|
|
123
|
+
devDirectory,
|
|
124
|
+
logger,
|
|
125
|
+
}),
|
|
126
|
+
runConfigTreeBuild({
|
|
127
|
+
configDirectory: path.join(root, 'app'),
|
|
128
|
+
outDirectory: path.join(root, 'builds', 'head'),
|
|
129
|
+
devDirectory,
|
|
130
|
+
logger,
|
|
131
|
+
}),
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
expect(base).toEqual({ status: 'ok', errors: [], warnings: [] });
|
|
135
|
+
expect(head).toEqual({ status: 'ok', errors: [], warnings: [] });
|
|
136
|
+
expect(hashTree(devDirectory)).toEqual(before);
|
|
137
|
+
expect(fs.readFileSync(path.join(devDirectory, 'public', 'logo.svg'), 'utf8')).toEqual(
|
|
138
|
+
'<svg>live</svg>'
|
|
139
|
+
);
|
|
140
|
+
const headPage = fs.readFileSync(
|
|
141
|
+
path.join(root, 'builds', 'head', 'build', 'pages', 'home.json'),
|
|
142
|
+
'utf8'
|
|
143
|
+
);
|
|
144
|
+
const basePage = fs.readFileSync(
|
|
145
|
+
path.join(root, 'builds', 'base', 'build', 'pages', 'home.json'),
|
|
146
|
+
'utf8'
|
|
147
|
+
);
|
|
148
|
+
expect(headPage).toContain('Head');
|
|
149
|
+
expect(basePage).toContain('Base');
|
|
150
|
+
expect(
|
|
151
|
+
JSON.parse(fs.readFileSync(path.join(root, 'builds', 'head', 'result.json'), 'utf8')).status
|
|
152
|
+
).toEqual('ok');
|
|
153
|
+
expect(
|
|
154
|
+
fs.lstatSync(path.join(root, 'builds', 'head', 'server', 'node_modules')).isSymbolicLink()
|
|
155
|
+
).toBe(true);
|
|
156
|
+
const configText = JSON.parse(
|
|
157
|
+
fs.readFileSync(path.join(root, 'builds', 'head', 'configText.json'), 'utf8')
|
|
158
|
+
);
|
|
159
|
+
expect(configText).toEqual(expect.arrayContaining(['Head', 'OK', 'Cancel']));
|
|
160
|
+
expect(configText).not.toContain('Base');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('runConfigTreeBuild reports a plugin the dev directory does not install as a build error', async () => {
|
|
164
|
+
const devDirectory = path.join(root, 'app', '.lowdefy', 'dev');
|
|
165
|
+
writeApp({ directory: path.join(root, 'app'), title: 'Head' });
|
|
166
|
+
fs.appendFileSync(
|
|
167
|
+
path.join(root, 'app', 'lowdefy.yaml'),
|
|
168
|
+
'plugins:\n - name: "@acme/blocks-not-installed"\n version: 1.2.0\n'
|
|
169
|
+
);
|
|
170
|
+
writeDevDirectory({ directory: devDirectory });
|
|
171
|
+
|
|
172
|
+
const result = await runConfigTreeBuild({
|
|
173
|
+
configDirectory: path.join(root, 'app'),
|
|
174
|
+
outDirectory: path.join(root, 'builds', 'head'),
|
|
175
|
+
devDirectory,
|
|
176
|
+
logger,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(result.status).toEqual('error');
|
|
180
|
+
expect(result.errors[0].message).toContain('@acme/blocks-not-installed');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('runConfigTreeBuild reports the config errors of a tree that does not build', async () => {
|
|
184
|
+
const devDirectory = path.join(root, 'app', '.lowdefy', 'dev');
|
|
185
|
+
writeApp({ directory: path.join(root, 'app'), title: 'Head' });
|
|
186
|
+
fs.writeFileSync(
|
|
187
|
+
path.join(root, 'app', 'lowdefy.yaml'),
|
|
188
|
+
'lowdefy: 6.0.0\npages:\n - id: home\n type: Buton\n'
|
|
189
|
+
);
|
|
190
|
+
writeDevDirectory({ directory: devDirectory });
|
|
191
|
+
|
|
192
|
+
const result = await runConfigTreeBuild({
|
|
193
|
+
configDirectory: path.join(root, 'app'),
|
|
194
|
+
outDirectory: path.join(root, 'builds', 'head'),
|
|
195
|
+
devDirectory,
|
|
196
|
+
logger,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(result.status).toEqual('error');
|
|
200
|
+
expect(result.errors.map((error) => error.message).join('\n')).toContain('Buton');
|
|
201
|
+
expect(fs.existsSync(path.join(root, 'builds', 'head', 'configText.json'))).toBe(false);
|
|
202
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Carries what a failed step expected and what it found, so the journey's
|
|
18
|
+
// failure report can show both. Executors throw it for a failed `expect` and
|
|
19
|
+
// for an interaction Playwright refused; the run loop turns it into
|
|
20
|
+
// `failure` and stops.
|
|
21
|
+
class JourneyStepError extends Error {
|
|
22
|
+
constructor(message, { expected, actual }) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'JourneyStepError';
|
|
25
|
+
this.expected = expected;
|
|
26
|
+
this.actual = actual;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default JourneyStepError;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { acquireMachineSlot } from '@lowdefy/node-utils';
|
|
18
|
+
|
|
19
|
+
const BROWSER_SLOTS = 3;
|
|
20
|
+
const BROWSER_SLOT_WAIT_MS = 5 * 60 * 1000;
|
|
21
|
+
|
|
22
|
+
// Takes one of the machine's browser slots, so at most three browser
|
|
23
|
+
// operations run at once across every dev server on the machine. The caller
|
|
24
|
+
// releases it (slot.release()) when its operation ends. Throws when the wait
|
|
25
|
+
// outlasts BROWSER_SLOT_WAIT_MS.
|
|
26
|
+
function acquireBrowserSlot({ acquire = acquireMachineSlot } = {}) {
|
|
27
|
+
return acquire({ name: 'browser', limit: BROWSER_SLOTS, waitMs: BROWSER_SLOT_WAIT_MS });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default acquireBrowserSlot;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
import readRoutineSteps from './readRoutineSteps.js';
|
|
20
|
+
|
|
21
|
+
// The browser never sees a routine's CallApi, in-process or detached, so the
|
|
22
|
+
// endpoints a journey reached through one are added from config: every
|
|
23
|
+
// endpoint a called endpoint's routine names with a literal endpointId,
|
|
24
|
+
// transitively, as { endpointId, via, calls: null }. A computed endpointId
|
|
25
|
+
// cannot be followed; such steps are counted as `unfollowed`.
|
|
26
|
+
async function addNestedEndpoints({ endpoints, readConfigFile }) {
|
|
27
|
+
const result = endpoints.map((endpoint) => ({ ...endpoint }));
|
|
28
|
+
const visited = new Set(endpoints.map(({ endpointId }) => endpointId));
|
|
29
|
+
const queue = [...visited];
|
|
30
|
+
let unfollowed = 0;
|
|
31
|
+
while (queue.length > 0) {
|
|
32
|
+
const caller = queue.shift();
|
|
33
|
+
const artifact = await readConfigFile(`api/${caller}.json`);
|
|
34
|
+
const callApiSteps = readRoutineSteps({ routine: artifact.routine }).filter(
|
|
35
|
+
(step) => step.type === 'CallApi'
|
|
36
|
+
);
|
|
37
|
+
callApiSteps.forEach((step) => {
|
|
38
|
+
const endpointId = step.properties?.endpointId;
|
|
39
|
+
if (!type.isString(endpointId)) {
|
|
40
|
+
unfollowed += 1;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (visited.has(endpointId)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
visited.add(endpointId);
|
|
47
|
+
queue.push(endpointId);
|
|
48
|
+
result.push({ endpointId, via: caller, calls: null });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return { endpoints: result, unfollowed };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default addNestedEndpoints;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import addNestedEndpoints from './addNestedEndpoints.js';
|
|
18
|
+
import addWriteFlags from './addWriteFlags.js';
|
|
19
|
+
import collectExercised from './collectExercised.js';
|
|
20
|
+
import readRoutineSteps from './readRoutineSteps.js';
|
|
21
|
+
|
|
22
|
+
function callApi({ id, endpointId, detached }) {
|
|
23
|
+
return {
|
|
24
|
+
id: `endpoint:x:${id}`,
|
|
25
|
+
stepId: id,
|
|
26
|
+
type: 'CallApi',
|
|
27
|
+
properties: { endpointId, ...(detached ? { detached: true } : {}) },
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const artifacts = {
|
|
32
|
+
'routes.json': [
|
|
33
|
+
{ pageId: 'tickets', path: 'tickets' },
|
|
34
|
+
{ pageId: 'ticket', path: 'tickets/{ticket_id}' },
|
|
35
|
+
],
|
|
36
|
+
'plugins/requestSchemas.json': {
|
|
37
|
+
MongoDBFind: { meta: { checkRead: true, checkWrite: false } },
|
|
38
|
+
MongoDBInsertOne: { meta: { checkRead: false, checkWrite: true } },
|
|
39
|
+
},
|
|
40
|
+
'pages/tickets/requests/list.json': { type: 'MongoDBFind' },
|
|
41
|
+
'pages/tickets/requests/save.json': { type: 'MongoDBInsertOne' },
|
|
42
|
+
// Writes only through the endpoints it calls.
|
|
43
|
+
'api/notify.json': {
|
|
44
|
+
routine: [
|
|
45
|
+
callApi({ id: 'inline', endpointId: 'log' }),
|
|
46
|
+
{ ':if': true, ':then': [callApi({ id: 'later', endpointId: 'archive', detached: true })] },
|
|
47
|
+
callApi({ id: 'computed', endpointId: { _payload: 'target' } }),
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
'api/log.json': {
|
|
51
|
+
routine: [
|
|
52
|
+
{ id: 'endpoint:log:insert', stepId: 'insert', type: 'MongoDBInsertOne' },
|
|
53
|
+
// A cycle back to the caller.
|
|
54
|
+
callApi({ id: 'back', endpointId: 'notify' }),
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
'api/archive.json': {
|
|
58
|
+
routine: [{ id: 'endpoint:archive:find', stepId: 'find', type: 'MongoDBFind' }],
|
|
59
|
+
},
|
|
60
|
+
'api/read.json': {
|
|
61
|
+
routine: [
|
|
62
|
+
{ ':try': [{ id: 'endpoint:read:find', stepId: 'find', type: 'MongoDBFind' }], ':catch': [] },
|
|
63
|
+
callApi({ id: 'archive', endpointId: 'archive' }),
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
async function readConfigFile(name) {
|
|
69
|
+
return artifacts[name] ?? null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
test('readRoutineSteps finds steps inside control objects and not inside step properties', () => {
|
|
73
|
+
const steps = readRoutineSteps({
|
|
74
|
+
routine: [
|
|
75
|
+
{ ':if': true, ':then': [{ stepId: 'a', type: 'A' }], ':else': [{ stepId: 'b', type: 'B' }] },
|
|
76
|
+
{ stepId: 'c', type: 'C', properties: { nested: [{ stepId: 'd', type: 'D' }] } },
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
expect(steps.map((step) => step.stepId)).toEqual(['a', 'b', 'c']);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('addNestedEndpoints follows in-process and detached CallApi with literal ids, transitively, once each', async () => {
|
|
83
|
+
const result = await addNestedEndpoints({
|
|
84
|
+
endpoints: [{ endpointId: 'notify', calls: 1 }],
|
|
85
|
+
readConfigFile,
|
|
86
|
+
});
|
|
87
|
+
expect(result).toEqual({
|
|
88
|
+
endpoints: [
|
|
89
|
+
{ endpointId: 'notify', calls: 1 },
|
|
90
|
+
{ endpointId: 'log', via: 'notify', calls: null },
|
|
91
|
+
{ endpointId: 'archive', via: 'notify', calls: null },
|
|
92
|
+
],
|
|
93
|
+
unfollowed: 1,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('addNestedEndpoints keeps an endpoint the browser called itself as called, not nested', async () => {
|
|
98
|
+
const result = await addNestedEndpoints({
|
|
99
|
+
endpoints: [
|
|
100
|
+
{ endpointId: 'notify', calls: 1 },
|
|
101
|
+
{ endpointId: 'archive', calls: 2 },
|
|
102
|
+
],
|
|
103
|
+
readConfigFile,
|
|
104
|
+
});
|
|
105
|
+
expect(result.endpoints).toContainEqual({ endpointId: 'archive', calls: 2 });
|
|
106
|
+
expect(result.endpoints.filter((entry) => entry.endpointId === 'archive')).toHaveLength(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('addWriteFlags follows checkWrite for a read and a write request type', async () => {
|
|
110
|
+
const { requests } = await addWriteFlags({
|
|
111
|
+
requests: [
|
|
112
|
+
{ pageId: 'tickets', requestId: 'list', calls: 1 },
|
|
113
|
+
{ pageId: 'tickets', requestId: 'save', calls: 1 },
|
|
114
|
+
],
|
|
115
|
+
endpoints: [],
|
|
116
|
+
readConfigFile,
|
|
117
|
+
requestSchemas: artifacts['plugins/requestSchemas.json'],
|
|
118
|
+
});
|
|
119
|
+
expect(requests).toEqual([
|
|
120
|
+
{ pageId: 'tickets', requestId: 'list', calls: 1, write: false },
|
|
121
|
+
{ pageId: 'tickets', requestId: 'save', calls: 1, write: true },
|
|
122
|
+
]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('addWriteFlags marks a caller that writes only through a nested endpoint as a write', async () => {
|
|
126
|
+
const { endpoints } = await addWriteFlags({
|
|
127
|
+
requests: [],
|
|
128
|
+
endpoints: [
|
|
129
|
+
{ endpointId: 'notify', calls: 1 },
|
|
130
|
+
{ endpointId: 'log', via: 'notify', calls: null },
|
|
131
|
+
{ endpointId: 'archive', via: 'notify', calls: null },
|
|
132
|
+
{ endpointId: 'read', calls: 1 },
|
|
133
|
+
],
|
|
134
|
+
readConfigFile,
|
|
135
|
+
requestSchemas: artifacts['plugins/requestSchemas.json'],
|
|
136
|
+
});
|
|
137
|
+
expect(endpoints.map(({ endpointId, write }) => [endpointId, write])).toEqual([
|
|
138
|
+
['notify', true],
|
|
139
|
+
['log', true],
|
|
140
|
+
['archive', false],
|
|
141
|
+
['read', false],
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('collectExercised merges actors, adds nested endpoints and writes, and passes the observed events and blocks through', async () => {
|
|
146
|
+
const exercised = await collectExercised({
|
|
147
|
+
snapshots: [
|
|
148
|
+
{
|
|
149
|
+
pagePaths: ['tickets', 'tickets/7'],
|
|
150
|
+
appEvents: true,
|
|
151
|
+
requests: [{ pageId: 'tickets', requestId: 'save', calls: 1 }],
|
|
152
|
+
endpoints: [{ endpointId: 'notify', calls: 1 }],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
observed: {
|
|
156
|
+
events: [
|
|
157
|
+
{
|
|
158
|
+
scope: 'page',
|
|
159
|
+
pageId: 'tickets',
|
|
160
|
+
blockId: 'save',
|
|
161
|
+
eventName: 'onClick',
|
|
162
|
+
actionIds: ['a'],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
rendered: { tickets: ['save'] },
|
|
166
|
+
},
|
|
167
|
+
readConfigFile,
|
|
168
|
+
requestSchemas: artifacts['plugins/requestSchemas.json'],
|
|
169
|
+
});
|
|
170
|
+
expect(exercised).toEqual({
|
|
171
|
+
pages: ['ticket', 'tickets'],
|
|
172
|
+
appEvents: true,
|
|
173
|
+
requests: [{ pageId: 'tickets', requestId: 'save', calls: 1, write: true }],
|
|
174
|
+
endpoints: [
|
|
175
|
+
{ endpointId: 'notify', calls: 1, write: true },
|
|
176
|
+
{ endpointId: 'log', via: 'notify', calls: null, write: true },
|
|
177
|
+
{ endpointId: 'archive', via: 'notify', calls: null, write: false },
|
|
178
|
+
],
|
|
179
|
+
unfollowed: 1,
|
|
180
|
+
events: [
|
|
181
|
+
{ scope: 'page', pageId: 'tickets', blockId: 'save', eventName: 'onClick', actionIds: ['a'] },
|
|
182
|
+
],
|
|
183
|
+
rendered: { tickets: ['save'] },
|
|
184
|
+
});
|
|
185
|
+
});
|