UNPKG

respond-framework

Version:
124 lines (116 loc) 5.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _combineInputEvents = require("../../modules/replayTools/helpers/combineInputEvents.js"); var _isEqual = require("../../utils/isEqual.js"); var _hydrateModules = require("../hydrateModules.js"); var _changePath = require("../../history/changePath.js"); var _constants = require("../../helpers/constants.js"); var _browserState = require("../../history/browserState.js"); var _reserved = require("../reserved.js"); var _url = require("../../helpers/url.js"); const trigger = function (state, e) { if (!e.meta.trigger) return; const { topState, replayState } = state.respond; const { respond, replayTools } = topState; const isSession = replayState.status === 'session'; const { skipped } = e.meta; replayState.status = 'reload'; respond.mem.changedPath = false; if (!skipped) state.respond.e = e; if (_constants.hasHistory && _browserState.default.maxIndex < 2 && !e.event.pattern && !respond.history.state.pop) { const { url } = (0, _url.urlToLocation)(window.location); (0, _changePath.push)(url); // optimization / browser history workaround: push the same url for first 2 non-navigation events, so history trap is enabled after first navigation event, where it usually wouldn't be (because it requires 2 pushes to become enabled) } if (e.event[_reserved._branch] === 'replayTools' && !replayTools.config.log) { (0, _hydrateModules.mergePrevState)(replayTools, respond.snapshot(replayTools)); return; } if (!skipped) { (0, _hydrateModules.mergePrevState)(topState, respond.snapshot(topState)); } if (!replayTools || _constants.isTest) return; const refresh = isSession && (_constants.isNative || respond.prevUrl === respond.fromEvent(e)?.url); // don't append a duplicate event to replayTools UI on refresh, but still dispatch it so its fx can run -- also note that native doesn't have an address bar to if (!refresh) { sendTrigger(e, replayTools, topState); } if (skipped) { // respond.devtools.forceNotification({ ...e, __prefix: '-- ' }) return false; } }; var _default = exports.default = trigger; const sendTrigger = (e, state, topState) => { const index = ++state.evsIndex; if (state.playing) { state.evs[index] = e; // event from tests isn't fully created yet, so we need to manually add it to the events array return; // during replays we preserve the events array, but move through it by index only, so you can see completed events in green, and yet to be dispatched rows in white (or purple) } const events = state.evs; const prev = events[index - 1]; const dispatchedSameAsSkippedEvent = prev?.meta?.skipped && isEqual(prev, e, topState); if (dispatchedSameAsSkippedEvent) { delete prev.meta.skipped; // ux optimization: user desired to unskip it by manually performing the same event return; } if (state.spliceMode) return handleSpliceMode(e, state, events, index, topState); const lastEntryIndex = events.length - 1; const shouldClipTail = index <= lastEntryIndex; if (shouldClipTail) { const dispatchedSameEvent = clipTail(e, state, events, index, topState); if (dispatchedSameEvent) return; // ux optimization: do nothing, as index increment resolves this automatically } if (inputConverged(e, state, events)) return; // ux optimization: undo divergence + combine into single input event if user manually enters same input value after multiple keystrokes! events.push(e); }; // helpers const handleSpliceMode = (e, state, events, index, topState) => { if (!events[index]) return events.push(e); // already at tail if (isEqual(events[index], e, topState)) return; // dispatchedSameEvent events.splice(index, 0, e); if (!state.divergentIndex || index < state.divergentIndex) { state.divergentIndex = index; } }; const clipTail = (e, state, events, index, topState) => { const next = events[index]; if (isEqual(next, e, topState)) return true; // user manually performed next event in sequence, so act as if there was no divergence events.splice(index); state.divergentIndex = index; }; const isEqual = (a, b, topState) => { if (a.event.type !== b.event.type) return false; const arg = topState.respond.revive(a.arg || {}); // revive possible event function references in test arg return (0, _isEqual.isEqualDeepPartial)(arg, b.arg); // e.arg may have some unrelated nested functions -- matching everything in arg works well for this case }; const inputConverged = (e, state, events) => { const { tests, selectedTestId, divergentIndex } = state; const possibleConvergingInputEvent = e.meta.input && divergentIndex !== undefined; const test = possibleConvergingInputEvent && tests[selectedTestId]; if (!test) return; const eventsCombined = (0, _combineInputEvents.default)([...events, e]); const eventsFromTestSoFar = test.events.slice(0, eventsCombined.length); const manuallyEnteredInputValues = (0, _isEqual.isEqualDeepPartial)(eventsFromTestSoFar, eventsCombined); // save some cycles, and don't revive which likely isn't necessary for input events if (manuallyEnteredInputValues) { state.evs = test.events; // display all events from test, having only tested isEqual on the number of events dispatched factoring in combined form events state.evsIndex = eventsCombined.length - 1; // there will be less events won't combined, and the last event is now the index delete state.divergentIndex; // no longer divergent! return true; } };