@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,148 @@
|
|
|
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 { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
18
|
+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
|
|
19
|
+
|
|
20
|
+
import { jest } from '@jest/globals';
|
|
21
|
+
|
|
22
|
+
import setupTestFixtures from './setupTestFixtures.mjs';
|
|
23
|
+
|
|
24
|
+
const fixtureDir = setupTestFixtures();
|
|
25
|
+
process.chdir(fixtureDir);
|
|
26
|
+
|
|
27
|
+
// An app served under a basePath: lowdefy_run_journey must hand it to the
|
|
28
|
+
// runner, which counts calls and app errors under it.
|
|
29
|
+
jest.unstable_mockModule('../build/config.js', () => ({ default: { basePath: '/app' } }));
|
|
30
|
+
const mockRunJourney = jest.fn();
|
|
31
|
+
jest.unstable_mockModule('./runJourney.js', () => ({ default: mockRunJourney }));
|
|
32
|
+
|
|
33
|
+
const { default: createDocsMcpServer } = await import('./createDocsMcpServer.js');
|
|
34
|
+
|
|
35
|
+
async function connectClient() {
|
|
36
|
+
const server = createDocsMcpServer({ origin: 'http://localhost:3000', version: '7.2.0' });
|
|
37
|
+
const client = new Client({ name: 'test-client', version: '1.0.0' });
|
|
38
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
39
|
+
await server.connect(serverTransport);
|
|
40
|
+
await client.connect(clientTransport);
|
|
41
|
+
return client;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
mockRunJourney.mockResolvedValue({ pageId: 'ticket', passed: true, steps: [], screenshots: [] });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('MCP lowdefy_run_journey passes pathParams and the basePath to every run of a user list', async () => {
|
|
50
|
+
const client = await connectClient();
|
|
51
|
+
|
|
52
|
+
await client.callTool({
|
|
53
|
+
name: 'lowdefy_run_journey',
|
|
54
|
+
arguments: {
|
|
55
|
+
pageId: 'ticket',
|
|
56
|
+
pathParams: { ticket_id: 't1' },
|
|
57
|
+
steps: [],
|
|
58
|
+
data: 'crm',
|
|
59
|
+
user: ['admin', 'member'],
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(mockRunJourney).toHaveBeenCalledTimes(2);
|
|
64
|
+
mockRunJourney.mock.calls.forEach(([params]) => {
|
|
65
|
+
expect(params).toEqual(
|
|
66
|
+
expect.objectContaining({
|
|
67
|
+
pageId: 'ticket',
|
|
68
|
+
pathParams: { ticket_id: 't1' },
|
|
69
|
+
data: 'crm',
|
|
70
|
+
basePath: '/app',
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
await client.close();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('MCP lowdefy_run_journey passes pathParams and the basePath to a single run', async () => {
|
|
78
|
+
const client = await connectClient();
|
|
79
|
+
|
|
80
|
+
await client.callTool({
|
|
81
|
+
name: 'lowdefy_run_journey',
|
|
82
|
+
arguments: { pageId: 'ticket', pathParams: { ticket_id: 't1' }, steps: [] },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(mockRunJourney).toHaveBeenCalledWith(
|
|
86
|
+
expect.objectContaining({ pathParams: { ticket_id: 't1' }, basePath: '/app' })
|
|
87
|
+
);
|
|
88
|
+
await client.close();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const fullExercised = {
|
|
92
|
+
pages: [{ pageId: 'ticket' }],
|
|
93
|
+
requests: [{ id: 'get_a' }, { id: 'get_b' }],
|
|
94
|
+
endpoints: [],
|
|
95
|
+
events: [{ id: 'e1' }, { id: 'e2' }, { id: 'e3' }],
|
|
96
|
+
rendered: [{ blockId: 'a' }],
|
|
97
|
+
unfollowed: [],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
async function callWith(extra, user) {
|
|
101
|
+
mockRunJourney.mockResolvedValue({
|
|
102
|
+
pageId: 'ticket',
|
|
103
|
+
passed: true,
|
|
104
|
+
steps: [],
|
|
105
|
+
screenshots: [],
|
|
106
|
+
exercised: fullExercised,
|
|
107
|
+
});
|
|
108
|
+
const client = await connectClient();
|
|
109
|
+
const result = await client.callTool({
|
|
110
|
+
name: 'lowdefy_run_journey',
|
|
111
|
+
arguments: { pageId: 'ticket', steps: [], ...(user ? { user, data: 'crm' } : {}), ...extra },
|
|
112
|
+
});
|
|
113
|
+
await client.close();
|
|
114
|
+
return JSON.parse(result.content[0].text);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
test('MCP lowdefy_run_journey returns a count of each exercised list by default', async () => {
|
|
118
|
+
const body = await callWith({});
|
|
119
|
+
expect(body.exercised.counts).toEqual({
|
|
120
|
+
pages: 1,
|
|
121
|
+
requests: 2,
|
|
122
|
+
endpoints: 0,
|
|
123
|
+
events: 3,
|
|
124
|
+
rendered: 1,
|
|
125
|
+
unfollowed: 0,
|
|
126
|
+
});
|
|
127
|
+
expect(body.exercised.requests).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('MCP lowdefy_run_journey returns the full exercised block with exercised: true', async () => {
|
|
131
|
+
const body = await callWith({ exercised: true });
|
|
132
|
+
expect(body.exercised).toEqual(fullExercised);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('MCP lowdefy_run_journey leaves exercised out with exercised: false', async () => {
|
|
136
|
+
const body = await callWith({ exercised: false });
|
|
137
|
+
expect(body.exercised).toBeUndefined();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('MCP lowdefy_run_journey applies exercised to every run of a user list', async () => {
|
|
141
|
+
const compact = await callWith({}, ['admin', 'member']);
|
|
142
|
+
expect(compact.runs).toHaveLength(2);
|
|
143
|
+
compact.runs.forEach((run) => expect(run.exercised.counts.events).toBe(3));
|
|
144
|
+
const none = await callWith({ exercised: false }, ['admin', 'member']);
|
|
145
|
+
none.runs.forEach((run) => expect(run.exercised).toBeUndefined());
|
|
146
|
+
const full = await callWith({ exercised: true }, ['admin', 'member']);
|
|
147
|
+
full.runs.forEach((run) => expect(run.exercised).toEqual(fullExercised));
|
|
148
|
+
});
|
|
@@ -0,0 +1,227 @@
|
|
|
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 http from 'node:http';
|
|
18
|
+
|
|
19
|
+
import { jest } from '@jest/globals';
|
|
20
|
+
|
|
21
|
+
import pageReadyScript from '../../test-utils/pageReadyScript.mjs';
|
|
22
|
+
|
|
23
|
+
// A real Chromium against a small server that answers the dev server's own
|
|
24
|
+
// routes, so the context-level request listener is exercised as a journey
|
|
25
|
+
// runs it: installed before the first navigation, and still counting after a
|
|
26
|
+
// full page load. Skipped when no Chromium can be launched.
|
|
27
|
+
jest.unstable_mockModule('../build/config.js', () => ({ default: {} }));
|
|
28
|
+
|
|
29
|
+
const { getBrowser } = await import('./getBrowser.js');
|
|
30
|
+
const { default: createJourneyActors } = await import('./createJourneyActors.js');
|
|
31
|
+
|
|
32
|
+
function pageHtml(script) {
|
|
33
|
+
return `<!doctype html><html><head><title>loading</title></head><body><script>${script}</script></body></html>`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const recorderPosts = [];
|
|
37
|
+
|
|
38
|
+
// Each page marks itself ready once its requests finish (pageReadyScript), so
|
|
39
|
+
// openPage returns then instead of waiting out its timeout.
|
|
40
|
+
const pages = {
|
|
41
|
+
'/recorded': pageHtml(`
|
|
42
|
+
window.__lowdefyRecorder = {
|
|
43
|
+
flush: () => fetch('/api/dev-recording', { method: 'POST', body: JSON.stringify({ actor: document.cookie }) }),
|
|
44
|
+
};
|
|
45
|
+
${pageReadyScript({ pageId: 'recorded' })}
|
|
46
|
+
document.title = 'recorded-ready';
|
|
47
|
+
`),
|
|
48
|
+
'/first': pageHtml(`
|
|
49
|
+
fetch('/api/root')
|
|
50
|
+
.then(() => fetch('/api/page/first'))
|
|
51
|
+
.then(() => fetch('/api/request/first/save', { method: 'POST' }))
|
|
52
|
+
.then(() => fetch('/api/endpoints/notify', { method: 'POST' }))
|
|
53
|
+
.then(() => {
|
|
54
|
+
${pageReadyScript({ pageId: 'first' })}
|
|
55
|
+
document.title = 'first-done';
|
|
56
|
+
});
|
|
57
|
+
`),
|
|
58
|
+
'/second': pageHtml(`
|
|
59
|
+
fetch('/api/page/second')
|
|
60
|
+
.then(() => fetch('/api/request/first/save', { method: 'POST' }))
|
|
61
|
+
.then(() => {
|
|
62
|
+
${pageReadyScript({ pageId: 'second' })}
|
|
63
|
+
document.title = 'second-done';
|
|
64
|
+
});
|
|
65
|
+
`),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// A cold Chromium on a Windows runner can take seconds over the first
|
|
69
|
+
// navigation; what is pinned here is what the actors count, not page speed.
|
|
70
|
+
const PAGE_TIMEOUT = 15000;
|
|
71
|
+
|
|
72
|
+
let server;
|
|
73
|
+
let origin;
|
|
74
|
+
let browser = null;
|
|
75
|
+
try {
|
|
76
|
+
browser = await getBrowser();
|
|
77
|
+
} catch {
|
|
78
|
+
browser = null;
|
|
79
|
+
}
|
|
80
|
+
const chromiumTest = browser === null ? test.skip : test;
|
|
81
|
+
|
|
82
|
+
beforeAll(async () => {
|
|
83
|
+
server = http.createServer((req, res) => {
|
|
84
|
+
if (req.url === '/api/dev-recording') {
|
|
85
|
+
recorderPosts.push({ cookie: req.headers.cookie ?? '' });
|
|
86
|
+
}
|
|
87
|
+
const html = pages[req.url];
|
|
88
|
+
if (html !== undefined) {
|
|
89
|
+
res.writeHead(200, { 'content-type': 'text/html' });
|
|
90
|
+
res.end(html);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
94
|
+
res.end('{}');
|
|
95
|
+
});
|
|
96
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
97
|
+
origin = `http://127.0.0.1:${server.address().port}`;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Closing Chromium on Windows can take longer than Jest's 5 s hook default.
|
|
101
|
+
afterAll(async () => {
|
|
102
|
+
await browser?.close();
|
|
103
|
+
server.closeAllConnections();
|
|
104
|
+
await new Promise((resolve) => server.close(resolve));
|
|
105
|
+
}, 30000);
|
|
106
|
+
|
|
107
|
+
chromiumTest(
|
|
108
|
+
'journey actors count pages, request calls, endpoints and app events across a full page load',
|
|
109
|
+
async () => {
|
|
110
|
+
const actors = createJourneyActors({
|
|
111
|
+
browser,
|
|
112
|
+
origin,
|
|
113
|
+
basePath: '',
|
|
114
|
+
pageId: 'first',
|
|
115
|
+
user: 'none',
|
|
116
|
+
timeout: PAGE_TIMEOUT,
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
const { page } = await actors.switchTo('main');
|
|
120
|
+
await page.waitForFunction(() => document.title === 'first-done');
|
|
121
|
+
await page.goto(`${origin}/second`);
|
|
122
|
+
await page.waitForFunction(() => document.title === 'second-done');
|
|
123
|
+
|
|
124
|
+
expect(actors.countCalls({ request: 'save', pageId: 'first' })).toEqual(2);
|
|
125
|
+
expect(actors.countCalls({ endpoint: 'notify' })).toEqual(1);
|
|
126
|
+
expect(actors.networkSnapshots()).toEqual([
|
|
127
|
+
{
|
|
128
|
+
pagePaths: ['first', 'second'],
|
|
129
|
+
appEvents: true,
|
|
130
|
+
requests: [{ pageId: 'first', requestId: 'save', calls: 2 }],
|
|
131
|
+
endpoints: [{ endpointId: 'notify', calls: 1 }],
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
134
|
+
} finally {
|
|
135
|
+
await actors.closeAll();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
30000
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
chromiumTest(
|
|
142
|
+
'a second journey actor counts its own requests apart from the first',
|
|
143
|
+
async () => {
|
|
144
|
+
const actors = createJourneyActors({
|
|
145
|
+
browser,
|
|
146
|
+
origin,
|
|
147
|
+
basePath: '',
|
|
148
|
+
pageId: 'second',
|
|
149
|
+
user: 'none',
|
|
150
|
+
timeout: PAGE_TIMEOUT,
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
const main = await actors.switchTo('main');
|
|
154
|
+
await main.page.waitForFunction(() => document.title === 'second-done');
|
|
155
|
+
const other = await actors.switchTo('other');
|
|
156
|
+
await other.page.waitForFunction(() => document.title === 'second-done');
|
|
157
|
+
await other.page.reload();
|
|
158
|
+
await other.page.waitForFunction(() => document.title === 'second-done');
|
|
159
|
+
expect(actors.countCalls({ request: 'save', pageId: 'first' })).toEqual(2);
|
|
160
|
+
await actors.switchTo('main');
|
|
161
|
+
expect(actors.countCalls({ request: 'save', pageId: 'first' })).toEqual(1);
|
|
162
|
+
} finally {
|
|
163
|
+
await actors.closeAll();
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
30000
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
chromiumTest(
|
|
170
|
+
'recording actors carry a recording cookie with their actor name and flush before closing',
|
|
171
|
+
async () => {
|
|
172
|
+
const { default: readRecordingCookie } = await import(
|
|
173
|
+
'../server/recording/readRecordingCookie.js'
|
|
174
|
+
);
|
|
175
|
+
const actors = createJourneyActors({
|
|
176
|
+
browser,
|
|
177
|
+
origin,
|
|
178
|
+
basePath: '',
|
|
179
|
+
pageId: 'recorded',
|
|
180
|
+
user: 'none',
|
|
181
|
+
timeout: PAGE_TIMEOUT,
|
|
182
|
+
recording: {
|
|
183
|
+
source: 'journey',
|
|
184
|
+
run: { id: '20261003T151200Z-p0d4rm', by: 'test', journey: 'tests/journeys/a.yaml' },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
const { page } = await actors.switchTo('main');
|
|
189
|
+
await page.waitForFunction(() => document.title === 'recorded-ready');
|
|
190
|
+
await actors.switchTo('invitee');
|
|
191
|
+
await actors.flushRecordings();
|
|
192
|
+
} finally {
|
|
193
|
+
await actors.closeAll();
|
|
194
|
+
}
|
|
195
|
+
expect(recorderPosts).toHaveLength(2);
|
|
196
|
+
const runs = recorderPosts
|
|
197
|
+
.map(({ cookie }) => readRecordingCookie(cookie))
|
|
198
|
+
.map((recording) => recording.run.actor)
|
|
199
|
+
.sort();
|
|
200
|
+
expect(runs).toEqual(['invitee', 'main']);
|
|
201
|
+
},
|
|
202
|
+
30000
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
chromiumTest(
|
|
206
|
+
'a non-recording actor is marked off and a page with no recorder flushes without error',
|
|
207
|
+
async () => {
|
|
208
|
+
const actors = createJourneyActors({
|
|
209
|
+
browser,
|
|
210
|
+
origin,
|
|
211
|
+
basePath: '',
|
|
212
|
+
pageId: 'second',
|
|
213
|
+
user: 'none',
|
|
214
|
+
timeout: PAGE_TIMEOUT,
|
|
215
|
+
});
|
|
216
|
+
try {
|
|
217
|
+
const { context } = await actors.switchTo('main');
|
|
218
|
+
const cookies = await context.cookies();
|
|
219
|
+
const recording = cookies.find((cookie) => cookie.name === 'lowdefy_recording');
|
|
220
|
+
expect(recording.value.endsWith('.off')).toBe(true);
|
|
221
|
+
await expect(actors.flushRecordings()).resolves.toBeUndefined();
|
|
222
|
+
} finally {
|
|
223
|
+
await actors.closeAll();
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
30000
|
|
227
|
+
);
|
|
@@ -0,0 +1,213 @@
|
|
|
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 collectVisibleBlockIds from '../client/collectVisibleBlockIds.js';
|
|
20
|
+
import createJourneyObservations from './createJourneyObservations.js';
|
|
21
|
+
import createNetworkCounter from './createNetworkCounter.js';
|
|
22
|
+
import { openPage } from './getBrowser.js';
|
|
23
|
+
|
|
24
|
+
// The binding client/JourneyObserver.jsx looks for. A binding exists in every
|
|
25
|
+
// document of the context it is exposed on, across full page loads, and in no
|
|
26
|
+
// developer tab, so only a journey's own pages report what they ran.
|
|
27
|
+
const OBSERVE_BINDING = '__lowdefyJourneyObserve';
|
|
28
|
+
|
|
29
|
+
let actorCount = 0;
|
|
30
|
+
|
|
31
|
+
// Every actor is its own client, as two people on two devices are: auth rate
|
|
32
|
+
// limits count attempts per client address, and a whole suite run from one
|
|
33
|
+
// machine would otherwise share a single budget, so the tenth journey's
|
|
34
|
+
// sign-in fails with "Too many requests" because of the nine before it. Each
|
|
35
|
+
// actor gets the next address in 203.0.113.0/24 (reserved for documentation,
|
|
36
|
+
// never a real client), so one actor's rapid attempts are still limited the
|
|
37
|
+
// way one person's are.
|
|
38
|
+
function nextClientAddress() {
|
|
39
|
+
actorCount += 1;
|
|
40
|
+
return `203.0.113.${((actorCount - 1) % 254) + 1}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The people a journey acts as, each in its own browser context with its own
|
|
44
|
+
// cookie jar, so an owner and an invitee - or a member whose session stays
|
|
45
|
+
// open while the owner removes them - are signed in at the same time. An
|
|
46
|
+
// actor opens the journey's page, as the journey's user (or as the data set
|
|
47
|
+
// user of that name), the first time its name is switched to, and keeps its
|
|
48
|
+
// tab as it left it when the journey switches away and back. Every actor sees
|
|
49
|
+
// the same viewport and colour scheme. Each actor's context feeds its own
|
|
50
|
+
// network counter from before its first navigation, so what the journey
|
|
51
|
+
// touched is measured per actor and merged at the end. Each actor's pages also report, through the observe
|
|
52
|
+
// binding, the events that completed and the blocks that were ever visible,
|
|
53
|
+
// into one set of observations for the whole journey.
|
|
54
|
+
function createJourneyActors({
|
|
55
|
+
browser,
|
|
56
|
+
origin,
|
|
57
|
+
basePath,
|
|
58
|
+
pageId,
|
|
59
|
+
path,
|
|
60
|
+
pathParams,
|
|
61
|
+
user,
|
|
62
|
+
urlQuery,
|
|
63
|
+
width,
|
|
64
|
+
height,
|
|
65
|
+
colorScheme,
|
|
66
|
+
timeout,
|
|
67
|
+
dataCookie,
|
|
68
|
+
mutantCookie,
|
|
69
|
+
users = {},
|
|
70
|
+
mainActor,
|
|
71
|
+
recording,
|
|
72
|
+
onContext,
|
|
73
|
+
}) {
|
|
74
|
+
const actors = new Map();
|
|
75
|
+
const counters = new Map();
|
|
76
|
+
const observations = createJourneyObservations();
|
|
77
|
+
let currentName;
|
|
78
|
+
|
|
79
|
+
// An actor named after a data set user opens as that user; the journey's
|
|
80
|
+
// first actor, and any other name, opens as the journey's user.
|
|
81
|
+
function userFor(name) {
|
|
82
|
+
if (name !== mainActor && Object.prototype.hasOwnProperty.call(users, name)) {
|
|
83
|
+
return users[name];
|
|
84
|
+
}
|
|
85
|
+
return user;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function switchTo(name) {
|
|
89
|
+
if (!actors.has(name)) {
|
|
90
|
+
const counter = createNetworkCounter({ origin, basePath });
|
|
91
|
+
counters.set(name, counter);
|
|
92
|
+
const opened = await openPage({
|
|
93
|
+
browser,
|
|
94
|
+
origin,
|
|
95
|
+
pageId,
|
|
96
|
+
path,
|
|
97
|
+
pathParams,
|
|
98
|
+
user: userFor(name),
|
|
99
|
+
urlQuery,
|
|
100
|
+
width,
|
|
101
|
+
height,
|
|
102
|
+
colorScheme,
|
|
103
|
+
clientAddress: nextClientAddress(),
|
|
104
|
+
dataCookie,
|
|
105
|
+
mutantCookie,
|
|
106
|
+
recording: type.isUndefined(recording)
|
|
107
|
+
? undefined
|
|
108
|
+
: { ...recording, run: { ...recording.run, actor: name } },
|
|
109
|
+
onContext: async (context) => {
|
|
110
|
+
context.on('request', (request) => counter.record(request));
|
|
111
|
+
await context.exposeBinding(OBSERVE_BINDING, (source, message) =>
|
|
112
|
+
observations.receive(message)
|
|
113
|
+
);
|
|
114
|
+
// A caller that watches each actor's context (a journey run's app
|
|
115
|
+
// error buffers) hooks in before its first request.
|
|
116
|
+
if (!type.isUndefined(onContext)) {
|
|
117
|
+
onContext({ context, name });
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
timeout,
|
|
121
|
+
});
|
|
122
|
+
actors.set(name, opened);
|
|
123
|
+
}
|
|
124
|
+
currentName = name;
|
|
125
|
+
return actors.get(name);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function current() {
|
|
129
|
+
return actors.get(currentName);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function countCalls(query) {
|
|
133
|
+
return counters.get(currentName).countCalls(query);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function calledSince(query) {
|
|
137
|
+
return counters.get(currentName).calledSince(query);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// The first URL any actor's context tried to reach on another host of the dev server, which a
|
|
141
|
+
// data set journey must never do (see guardJourneyOrigin).
|
|
142
|
+
function leftOrigin() {
|
|
143
|
+
for (const opened of actors.values()) {
|
|
144
|
+
if (opened.leftOrigin.length > 0) {
|
|
145
|
+
return opened.leftOrigin[0];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function networkSnapshots() {
|
|
152
|
+
return [...counters.values()].map((counter) => counter.snapshot());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// The runner's own look at the current page once a step has settled, beside
|
|
156
|
+
// the observer's samples on every DOM change. A page that is navigating or
|
|
157
|
+
// has crashed has nothing to report.
|
|
158
|
+
async function sampleRendered() {
|
|
159
|
+
const { page } = current();
|
|
160
|
+
try {
|
|
161
|
+
const pageId = await page.evaluate(() => window.lowdefy?.pageId);
|
|
162
|
+
const blockIds = await page.evaluate(collectVisibleBlockIds);
|
|
163
|
+
observations.receive({ kind: 'rendered', pageId, blockIds });
|
|
164
|
+
} catch {
|
|
165
|
+
// Nothing to sample.
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function observed() {
|
|
170
|
+
return observations.snapshot();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Closing a context does not reliably fire pagehide, so each actor's dev
|
|
174
|
+
// recorder is flushed first, or the journey's last steps would be lost.
|
|
175
|
+
async function flushRecordings({ capMs = 2000 } = {}) {
|
|
176
|
+
await Promise.all(
|
|
177
|
+
[...actors.values()].map(async ({ page }) => {
|
|
178
|
+
// Cleared once the race settles, so a flush that wins does not leave
|
|
179
|
+
// the cap's timer holding the process open.
|
|
180
|
+
let cap;
|
|
181
|
+
try {
|
|
182
|
+
await Promise.race([
|
|
183
|
+
page.evaluate(() => window.__lowdefyRecorder?.flush()).catch(() => {}),
|
|
184
|
+
new Promise((resolve) => {
|
|
185
|
+
cap = setTimeout(resolve, capMs);
|
|
186
|
+
}),
|
|
187
|
+
]);
|
|
188
|
+
} finally {
|
|
189
|
+
clearTimeout(cap);
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function closeAll() {
|
|
196
|
+
await Promise.all([...actors.values()].map(({ context }) => context.close().catch(() => {})));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
switchTo,
|
|
201
|
+
current,
|
|
202
|
+
countCalls,
|
|
203
|
+
calledSince,
|
|
204
|
+
leftOrigin,
|
|
205
|
+
networkSnapshots,
|
|
206
|
+
sampleRendered,
|
|
207
|
+
observed,
|
|
208
|
+
flushRecordings,
|
|
209
|
+
closeAll,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export default createJourneyActors;
|
|
@@ -0,0 +1,79 @@
|
|
|
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 { jest } from '@jest/globals';
|
|
18
|
+
|
|
19
|
+
const mockOpenPage = jest.fn();
|
|
20
|
+
jest.unstable_mockModule('./getBrowser.js', () => ({ openPage: mockOpenPage }));
|
|
21
|
+
|
|
22
|
+
const { default: createJourneyActors } = await import('./createJourneyActors.js');
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
mockOpenPage.mockReset();
|
|
26
|
+
mockOpenPage.mockImplementation(async ({ user }) => ({
|
|
27
|
+
context: { close: jest.fn(async () => {}) },
|
|
28
|
+
page: {},
|
|
29
|
+
user,
|
|
30
|
+
}));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const users = {
|
|
34
|
+
main: { id: 'u_main', roles: ['member'] },
|
|
35
|
+
outsider: { id: 'u_9', roles: ['admin'], organizationId: 'org_b' },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function create(overrides) {
|
|
39
|
+
return createJourneyActors({
|
|
40
|
+
browser: {},
|
|
41
|
+
origin: 'http://localhost:3227',
|
|
42
|
+
basePath: '',
|
|
43
|
+
pageId: 'tickets',
|
|
44
|
+
user: { id: 'u_1', roles: ['admin'] },
|
|
45
|
+
dataCookie: 'session1',
|
|
46
|
+
users,
|
|
47
|
+
mainActor: 'main',
|
|
48
|
+
...overrides,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test('switchTo opens an actor named after a data set user as that user, with the data cookie', async () => {
|
|
53
|
+
const actors = create();
|
|
54
|
+
await actors.switchTo('outsider');
|
|
55
|
+
expect(mockOpenPage).toHaveBeenCalledWith(
|
|
56
|
+
expect.objectContaining({ user: users.outsider, dataCookie: 'session1' })
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('switchTo opens the main actor and any other name as the journey user', async () => {
|
|
61
|
+
const actors = create();
|
|
62
|
+
await actors.switchTo('main');
|
|
63
|
+
await actors.switchTo('reviewer');
|
|
64
|
+
expect(mockOpenPage.mock.calls.map(([options]) => options.user)).toEqual([
|
|
65
|
+
{ id: 'u_1', roles: ['admin'] },
|
|
66
|
+
{ id: 'u_1', roles: ['admin'] },
|
|
67
|
+
]);
|
|
68
|
+
expect(mockOpenPage.mock.calls.every(([options]) => options.dataCookie === 'session1')).toBe(
|
|
69
|
+
true
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('switchTo opens every actor as the journey user without data set users', async () => {
|
|
74
|
+
const actors = create({ users: undefined, dataCookie: undefined });
|
|
75
|
+
await actors.switchTo('outsider');
|
|
76
|
+
expect(mockOpenPage).toHaveBeenCalledWith(
|
|
77
|
+
expect.objectContaining({ user: { id: 'u_1', roles: ['admin'] }, dataCookie: undefined })
|
|
78
|
+
);
|
|
79
|
+
});
|