respond-framework
Version:
create as fast you think
124 lines (116 loc) • 5.61 kB
JavaScript
;
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;
}
};