diff --git a/.eslintrc b/.eslintrc
index e35e3c0f..f4700131 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -4,6 +4,7 @@
"browser": true,
"es6": true
},
+ "parser": "babel-eslint",
"ecmaFeatures": {
"modules": true,
"jsx": true
@@ -25,7 +26,7 @@
}],
"no-use-before-define": 2,
// possible errors
- "comma-dangle": [2, "never"],
+ "comma-dangle": [0, "always"],
"no-cond-assign": [2, "always"],
"no-debugger": 1,
"no-alert": 1,
diff --git a/package.json b/package.json
index da31853e..097ec1b7 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"babel": "^5.6.6",
"babel-core": "^5.6.6",
"babel-loader": "^5.1.4",
+ "babel-eslint": "3.1.18",
"chai": "^2.3.0",
"coveralls": "^2.11.2",
"envify": "^3.4.0",
diff --git a/src/alt/index.js b/src/alt/index.js
index b397b8da..c586f234 100644
--- a/src/alt/index.js
+++ b/src/alt/index.js
@@ -24,7 +24,15 @@ class Alt {
}
dispatch(action, data, details) {
- this.batchingFunction(() => this.dispatcher.dispatch({ action, data, details }))
+ this.batchingFunction(() => {
+ const id = Math.random().toString(18).substr(2, 16)
+ return this.dispatcher.dispatch({
+ id,
+ action,
+ data,
+ details
+ })
+ })
}
createUnsavedStore(StoreModel, ...args) {
diff --git a/src/utils/Debugger.js b/src/utils/Debugger.js
new file mode 100644
index 00000000..499b9159
--- /dev/null
+++ b/src/utils/Debugger.js
@@ -0,0 +1,30 @@
+/*eslint-disable */
+import DebugActions from './debug/DebugActions'
+import DispatcherDebugger from './DispatcherDebugger'
+import React from 'react'
+import StoreExplorer from './StoreExplorer'
+
+class Debugger extends React.Component {
+ componentDidMount() {
+ DebugActions.setAlt(this.props.alt)
+ }
+
+ renderInspectorWindow() {
+ return this.props.inspector
+ ?
+ : null
+ }
+
+ render() {
+ return (
+
+
Debug
+
+
+ {this.renderInspectorWindow()}
+
+ )
+ }
+}
+
+export default Debugger
diff --git a/src/utils/DispatcherDebugger.js b/src/utils/DispatcherDebugger.js
new file mode 100644
index 00000000..2ce32569
--- /dev/null
+++ b/src/utils/DispatcherDebugger.js
@@ -0,0 +1,196 @@
+/*eslint-disable */
+import React from 'react'
+import { Column, Table } from 'fixed-data-table'
+import makeFinalStore from './makeFinalStore'
+import connectToStores from './connectToStores'
+
+import FixedDataTableCss from './debug/FixedDataTableCss'
+
+import DebugActions from './debug/DebugActions'
+import DispatcherStore from './debug/DispatcherStore'
+
+class DispatcherDebugger extends React.Component {
+ constructor() {
+ super()
+
+ this.getDispatch = this.getDispatch.bind(this)
+ this.renderName = this.renderName.bind(this)
+ this.renderReplay = this.renderReplay.bind(this)
+ this.renderRevert = this.renderRevert.bind(this)
+ this.view = this.view.bind(this)
+ }
+
+ componentDidMount() {
+ const finalStore = makeFinalStore(this.props.alt)
+ finalStore.listen(state => DebugActions.addDispatch(state.payload))
+ DebugActions.setAlt(this.props.alt)
+ }
+
+ clear() {
+ DebugActions.clear()
+ }
+
+ getDispatch(idx) {
+ const dispatch = this.props.dispatches[idx]
+ return {
+ id: dispatch.id,
+ action: dispatch.action,
+ data: dispatch.data,
+ details: dispatch.details,
+ recorded: dispatch.recorded,
+ dispatchedStores: dispatch.dispatchedStores,
+ mtime: this.props.mtime,
+ }
+ }
+
+ loadRecording() {
+ const json = prompt('Give me a serialized recording')
+ if (json) DebugActions.loadRecording(json)
+ }
+
+ revert(ev) {
+ const data = ev.target.dataset
+ DebugActions.revert(data.dispatchId)
+ }
+
+ saveRecording() {
+ DebugActions.saveRecording()
+ }
+
+ startReplay() {
+ DebugActions.startReplay()
+ DebugActions.replay()
+ }
+
+ stopReplay() {
+ DebugActions.stopReplay()
+ }
+
+ toggleLogDispatches() {
+ DebugActions.toggleLogDispatches()
+ }
+
+ togglePauseReplay() {
+ DebugActions.togglePauseReplay()
+ }
+
+ toggleRecording() {
+ DebugActions.toggleRecording()
+ }
+
+ view(ev) {
+ const data = ev.target.dataset
+ const dispatch = this.props.dispatches[data.index]
+ DebugActions.selectData(dispatch)
+ }
+
+ renderName(name, _, dispatch, idx) {
+ return (
+
+ {name}
+
+ )
+ }
+
+ renderReplay() {
+ if (this.props.inReplayMode) {
+ return (
+
+
+ {this.props.isReplaying ? 'Pause Replay' : 'Resume Replay'}
+
+ {' | '}
+
+ Stop Replay
+
+
+ )
+ }
+
+ return (
+
+ Start Replay
+
+ )
+ }
+
+ renderRevert(a, b, dispatch) {
+ return (
+
+
+ Revert
+
+
+
+ )
+ }
+
+ render() {
+ return (
+
+
Dispatches
+
+
+
+ {this.props.isRecording ? 'Stop Recording' : 'Record'}
+
+ {' | '}
+
+ Clear
+
+ {' | '}
+
+ Save
+
+ {' | '}
+
+ Load
+
+ {' | '}
+ {this.renderReplay()}
+
+
+
+ )
+ }
+}
+
+export default connectToStores({
+ getPropsFromStores() {
+ return DispatcherStore.getState()
+ },
+
+ getStores() {
+ return [DispatcherStore]
+ }
+}, DispatcherDebugger)
diff --git a/src/utils/DispatcherRecorder.js b/src/utils/DispatcherRecorder.js
index f887b6f0..0d19f781 100644
--- a/src/utils/DispatcherRecorder.js
+++ b/src/utils/DispatcherRecorder.js
@@ -110,8 +110,9 @@ DispatcherRecorder.prototype.replay = function (replayTime, done) {
DispatcherRecorder.prototype.serializeEvents = function () {
const events = this.events.map((event) => {
return {
+ id: event.id,
action: event.action,
- data: event.data
+ data: event.data || {}
}
})
return JSON.stringify(events)
@@ -129,6 +130,7 @@ DispatcherRecorder.prototype.loadEvents = function (events) {
data: event.data
}
})
+ return parsedEvents
}
export default DispatcherRecorder
diff --git a/src/utils/Inspector.js b/src/utils/Inspector.js
new file mode 100644
index 00000000..b844761f
--- /dev/null
+++ b/src/utils/Inspector.js
@@ -0,0 +1,154 @@
+/*eslint-disable */
+import React from 'react'
+import ViewerStore from './debug/ViewerStore'
+import connectToStores from './connectToStores'
+
+const Styles = {
+ root: {
+ font: '14px/1.4 Consolas, monospace',
+ },
+
+ line: {
+ cursor: 'pointer',
+ paddingLeft: '1em',
+ },
+
+ key: {
+ color: '#656865',
+ },
+
+ string: {
+ color: '#87af5f',
+ cursor: 'text',
+ marginLeft: '0.1em',
+ },
+
+ boolean: {
+ color: '#f55e5f',
+ cursor: 'text',
+ marginLeft: '0.1em',
+ },
+
+ number: {
+ color: '#57b3df',
+ cursor: 'text',
+ marginLeft: '0.1em',
+ },
+
+ helper: {
+ color: '#b0b0b0',
+ marginLeft: '0.1em',
+ },
+}
+
+class Leaf extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ hidden: this.props.hidden
+ }
+
+ this.toggle = this._toggle.bind(this)
+ }
+
+ renderValue() {
+ if (typeof this.props.data === 'object' && this.props.data) {
+ if (this.state.hidden) {
+ return null
+ }
+
+ return Object.keys(this.props.data).map((node, i) => {
+ return (
+ 0}
+ />
+ )
+ })
+ } else {
+ const jstype = typeof this.props.data
+
+ return {String(this.props.data)}
+ }
+ }
+
+ renderPluralCount(n) {
+ return n === 0
+ ? ''
+ : n === 1 ? '1 item' : `${n} items`
+ }
+
+ renderLabel() {
+ const label = this.props.label || 'dispatch'
+
+ const jstype = typeof this.props.data
+
+ const type = jstype !== 'object'
+ ? ''
+ : Array.isArray(this.props.data) ? '[]' : '{}'
+
+ const length = jstype === 'object' && this.props.data != null
+ ? Object.keys(this.props.data).length
+ : 0
+
+ return (
+
+
+ {label}:
+
+
+ {type}
+ {' '}
+ {this.renderPluralCount(length)}
+
+
+ )
+ }
+
+ _toggle() {
+ this.setState({
+ hidden: !this.state.hidden
+ })
+ }
+
+ render() {
+ return (
+
+
+ {this.renderLabel()}
+
+ {this.renderValue()}
+
+ )
+ }
+}
+
+Leaf.defaultProps = { hidden: true }
+
+class Inspector extends React.Component {
+ constructor() {
+ super()
+ }
+
+ render() {
+ return (
+
+
+
+ )
+ }
+}
+
+export default connectToStores({
+ getPropsFromStores() {
+ return ViewerStore.getState()
+ },
+
+ getStores() {
+ return [ViewerStore]
+ }
+}, Inspector)
diff --git a/src/utils/StoreExplorer.js b/src/utils/StoreExplorer.js
new file mode 100644
index 00000000..06441341
--- /dev/null
+++ b/src/utils/StoreExplorer.js
@@ -0,0 +1,56 @@
+import AltStore from './debug/AltStore'
+import DebugActions from './debug/DebugActions'
+import React from 'react'
+import connectToStores from './connectToStores'
+
+class StoreExplorer extends React.Component {
+ constructor() {
+ super()
+
+ this.selectStore = this.selectStore.bind(this)
+ }
+
+ componentDidMount() {
+ DebugActions.setAlt(this.props.alt)
+ }
+
+ selectStore(ev) {
+ const data = ev.target.dataset
+ const store = this.props.alt.stores[data.name]
+ if (store) DebugActions.selectData(store.getState())
+ }
+
+ render() {
+ return (
+
+
Stores
+
+ {this.props.stores.map((store) => {
+ return (
+ -
+ {store.displayName}
+
+ )
+ })}
+
+
+ )
+ }
+}
+
+export default connectToStores({
+ getPropsFromStores() {
+ return {
+ stores: AltStore.stores()
+ }
+ },
+
+ getStores() {
+ return [AltStore]
+ }
+}, StoreExplorer)
diff --git a/src/utils/debug/AltStore.js b/src/utils/debug/AltStore.js
new file mode 100644
index 00000000..ae210367
--- /dev/null
+++ b/src/utils/debug/AltStore.js
@@ -0,0 +1,33 @@
+import alt from './alt'
+import DebugActions from './DebugActions'
+
+export default alt.createStore(class {
+ static displayName = 'AltStore'
+
+ static config = {
+ getState(state) {
+ return {
+ stores: state.stores
+ }
+ }
+ }
+
+ constructor() {
+ this.alt = null
+ this.stores = []
+
+ this.bindActions(DebugActions)
+
+ this.exportPublicMethods({
+ alt: () => this.alt,
+ stores: () => this.stores,
+ })
+ }
+
+ setAlt(altInst) {
+ this.alt = altInst
+ this.stores = Object.keys(this.alt.stores).map((name) => {
+ return this.alt.stores[name]
+ })
+ }
+})
diff --git a/src/utils/debug/DebugActions.js b/src/utils/debug/DebugActions.js
new file mode 100644
index 00000000..4d93773d
--- /dev/null
+++ b/src/utils/debug/DebugActions.js
@@ -0,0 +1,16 @@
+import alt from './alt'
+
+export default alt.generateActions(
+ 'addDispatch',
+ 'clear',
+ 'loadRecording',
+ 'replay',
+ 'revert',
+ 'saveRecording',
+ 'selectData',
+ 'setAlt',
+ 'startReplay',
+ 'stopReplay',
+ 'togglePauseReplay',
+ 'toggleRecording'
+)
diff --git a/src/utils/debug/DispatcherStore.js b/src/utils/debug/DispatcherStore.js
new file mode 100644
index 00000000..79c57854
--- /dev/null
+++ b/src/utils/debug/DispatcherStore.js
@@ -0,0 +1,135 @@
+import alt from './alt'
+import AltStore from './AltStore'
+import DebugActions from './DebugActions'
+
+export default alt.createStore(class {
+ static displayName = 'DispatcherStore'
+
+ static config = {
+ getState(state) {
+ return {
+ currentStateId: state.currentStateId,
+ dispatches: state.dispatches,
+ inReplayMode: state.nextReplayId !== null,
+ isRecording: state.isRecording,
+ isReplaying: state.isReplaying,
+ mtime: state.mtime,
+ }
+ }
+ }
+
+ constructor() {
+ this.cachedDispatches = []
+ this.dispatches = []
+ this.currentStateId = null
+ this.snapshots = {}
+ this.replayTime = 100
+ this.isRecording = true
+ this.isReplaying = false
+ this.nextReplayId = null
+
+ // due to the aggressive nature of FixedDataTable's shouldComponentUpdate
+ // and JS objects being references not values we need an mtime applied
+ // to each dispatch so we know when data has changed
+ this.mtime = Date.now()
+
+ this.on('beforeEach', () => {
+ this.mtime = Date.now()
+ })
+
+ this.bindActions(DebugActions)
+ }
+
+ addDispatch(payload) {
+ if (!this.isRecording) return false
+
+ const dispatchedStores = AltStore.stores()
+ .filter((x) => x.boundListeners.indexOf(payload.action) > -1)
+ .map((x) => x.name)
+ .join(', ')
+
+ payload.dispatchedStores = dispatchedStores
+
+ this.dispatches.unshift(payload)
+
+ this.snapshots[payload.id] = AltStore.alt().takeSnapshot()
+ this.currentStateId = payload.id
+ }
+
+ clear() {
+ this.dispatches = []
+ this.currentStateId = null
+ this.nextReplayId = null
+ this.snapshots = {}
+
+ AltStore.alt().recycle()
+ }
+
+ loadRecording(events) {
+ this.clear()
+ const wasRecording = this.isRecording
+ this.isRecording = true
+ const dispatches = JSON.parse(events)
+ dispatches.reverse().forEach((dispatch) => {
+ setTimeout(() => {
+ AltStore.alt().dispatch(
+ dispatch.action,
+ dispatch.data,
+ dispatch.details
+ )
+ }, 0)
+ })
+ this.isRecording = wasRecording
+ }
+
+ replay() {
+ if (!this.isReplaying) return false
+
+ const dispatch = this.cachedDispatches[this.nextReplayId]
+ setTimeout(() => {
+ AltStore.alt().dispatch(dispatch.action, dispatch.data, dispatch.details)
+ }, 0)
+
+ this.nextReplayId = this.nextReplayId - 1
+
+ if (this.nextReplayId >= 0) {
+ setTimeout(() => DebugActions.replay(), this.replayTime)
+ } else {
+ this.isReplaying = false
+ this.nextReplayId = null
+ }
+ }
+
+ revert(id) {
+ const snapshot = this.snapshots[id]
+ if (snapshot) {
+ this.currentStateId = id
+ AltStore.alt().bootstrap(snapshot)
+ }
+ }
+
+ saveRecording() {
+ console.log(JSON.stringify(this.dispatches))
+ }
+
+ startReplay() {
+ this.cachedDispatches = this.dispatches.slice()
+ this.clear()
+ this.nextReplayId = this.cachedDispatches.length - 1
+ this.isReplaying = true
+ }
+
+ stopReplay() {
+ this.cachedDispatches = []
+ this.nextReplayId = null
+ this.isReplaying = false
+ }
+
+ togglePauseReplay() {
+ this.isReplaying = !this.isReplaying
+ }
+
+ toggleRecording() {
+ this.isRecording = !this.isRecording
+ }
+})
diff --git a/src/utils/debug/FixedDataTableCss.js b/src/utils/debug/FixedDataTableCss.js
new file mode 100644
index 00000000..33caed42
--- /dev/null
+++ b/src/utils/debug/FixedDataTableCss.js
@@ -0,0 +1,32 @@
+import React from 'react'
+
+const css = `
+/**
+ * FixedDataTable v0.3.0
+ *
+ * Copyright (c) 2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+.public_Scrollbar_main{box-sizing:border-box;outline:none;overflow:hidden;position:absolute;-webkit-transition-duration:250ms;transition-duration:250ms;-webkit-transition-timing-function:ease;transition-timing-function:ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.public_Scrollbar_mainVertical{bottom:0;right:0;top:0;-webkit-transition-property:background-color width;transition-property:background-color width;width:15px}.public_Scrollbar_mainVertical.Scrollbar_mainActive,.public_Scrollbar_mainVertical:hover{background-color:rgba(255,255,255,.8);width:17px}.public_Scrollbar_mainHorizontal{bottom:0;height:15px;left:0;-webkit-transition-property:background-color height;transition-property:background-color height}.public_Scrollbar_mainHorizontal.Scrollbar_mainActive,.public_Scrollbar_mainHorizontal:hover{background-color:rgba(255,255,255,.8);height:17px}.Scrollbar_mainOpaque,.Scrollbar_mainOpaque.Scrollbar_mainActive,.Scrollbar_mainOpaque:hover{background-color:#fff}.Scrollbar_face{left:0;overflow:hidden;position:absolute;z-index:1}.Scrollbar_face:after{background-color:#c2c2c2;border-radius:6px;content:'';display:block;position:absolute;-webkit-transition:background-color 250ms ease;transition:background-color 250ms ease}.public_Scrollbar_main:hover .Scrollbar_face:after,.Scrollbar_mainActive .Scrollbar_face:after,.Scrollbar_faceActive:after{background-color:#7d7d7d}.Scrollbar_faceHorizontal{bottom:0;left:0;top:0}.Scrollbar_faceHorizontal:after{bottom:4px;left:0;top:4px;width:100%}.Scrollbar_faceVertical{left:0;right:0;top:0}.Scrollbar_faceVertical:after{height:100%;left:4px;right:4px;top:0}.public_fixedDataTable_main{border:solid 1px #d3d3d3;box-sizing:border-box;overflow:hidden;position:relative}.public_fixedDataTable_header,.fixedDataTable_hasBottomBorder{border-bottom:solid 1px #d3d3d3}.public_fixedDataTableRow_main.fixedDataTable_hasBottomBorder{box-sizing:content-box}.public_fixedDataTable_header .public_fixedDataTableCell_main{font-weight:700}.public_fixedDataTable_header,.public_fixedDataTable_header .public_fixedDataTableCell_main{background-color:#f6f7f8;background-image:-webkit-linear-gradient(#fff,#efefef);background-image:linear-gradient(#fff,#efefef)}.public_fixedDataTable_footer .public_fixedDataTableCell_main{background-color:#f6f7f8;border-top:solid 1px #d3d3d3}.fixedDataTable_topShadow,.fixedDataTable_bottomShadow{height:4px;left:0;position:absolute;right:0;z-index:1}.fixedDataTable_topShadow{background:0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAF0lEQVR4AWPUkNeSBhHCjJoK2twgFisAFagCCp3pJlAAAAAASUVORK5CYII=) repeat-x}.fixedDataTable_bottomShadow{background:0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAHElEQVQI12MwNjZmZdAT1+Nm0JDWEGZQk1GTBgAWkwIeAEp52AAAAABJRU5ErkJggg==) repeat-x;margin-top:-4px}.fixedDataTable_rowsContainer{overflow:hidden;position:relative}.fixedDataTable_horizontalScrollbar{bottom:0;position:absolute}.fixedDataTable_horizontalScrollbar .public_Scrollbar_mainHorizontal{background:#fff}.public_fixedDataTableCell_main{background-color:#fff;border:solid 1px #d3d3d3;border-width:0 1px 0 0;box-sizing:border-box;display:block;overflow:hidden;position:absolute;white-space:normal}.public_fixedDataTableCell_lastChild{border-width:0 1px 1px 0}.public_fixedDataTableCell_highlighted{background-color:#f4f4f4}.public_fixedDataTableCell_alignRight{text-align:right}.public_fixedDataTableCell_alignCenter{text-align:center}.public_fixedDataTableCell_wrap1{display:table}.public_fixedDataTableCell_wrap2{display:table-row}.public_fixedDataTableCell_wrap3{display:table-cell;vertical-align:middle}.public_fixedDataTableCell_cellContent{padding:8px}.fixedDataTableCell_columnResizerContainer{position:absolute;right:0;width:6px;z-index:1}.fixedDataTableCell_columnResizerContainer:hover{cursor:ew-resize}.fixedDataTableCell_columnResizerContainer:hover .fixedDataTableCell_columnResizerKnob{visibility:visible}.fixedDataTableCell_columnResizerKnob{background-color:#0284ff;position:absolute;right:0;visibility:hidden;width:4px}.fixedDataTableCellGroup_cellGroup{-webkit-backface-visibility:hidden;backface-visibility:hidden;left:0;overflow:hidden;position:absolute;top:0;white-space:nowrap}.fixedDataTableCellGroup_cellGroup>.public_fixedDataTableCell_main{display:inline-block;vertical-align:top;white-space:normal}.fixedDataTableCellGroup_cellGroupWrapper{position:absolute;top:0}.fixedDataTableColumnResizerLine_mouseArea{cursor:ew-resize;position:absolute;right:-5px;width:12px}.fixedDataTableColumnResizerLine_main{border-right:1px solid #0284ff;box-sizing:border-box;position:absolute;z-index:10}.fixedDataTableColumnResizerLine_hiddenElem{display:none!important}.public_fixedDataTableRow_main{background-color:#fff;box-sizing:border-box;overflow:hidden;position:absolute;top:0}.fixedDataTableRow_body{left:0;position:absolute;top:0}.public_fixedDataTableRow_highlighted,.public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main{background-color:#f6f7f8}.fixedDataTableRow_fixedColumnsDivider{-webkit-backface-visibility:hidden;backface-visibility:hidden;border-left:solid 1px #d3d3d3;left:0;position:absolute;top:0;width:0}.fixedDataTableRow_columnsShadow{background:0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAYAAAD5PA/NAAAAFklEQVQIHWPSkNeSBmJhTQVtbiDNCgASagIIuJX8OgAAAABJRU5ErkJggg==) repeat-y;width:4px}.fixedDataTableRow_rowWrapper{position:absolute;top:0}
+`
+
+class FixedDataTableCss extends React.Component {
+ shouldComponentUpdate() {
+ return false
+ }
+
+ render() {
+ return (
+
+ )
+ }
+}
+
+export default FixedDataTableCss
diff --git a/src/utils/debug/ViewerStore.js b/src/utils/debug/ViewerStore.js
new file mode 100644
index 00000000..8a808be7
--- /dev/null
+++ b/src/utils/debug/ViewerStore.js
@@ -0,0 +1,25 @@
+import alt from './alt'
+import DebugActions from './DebugActions'
+
+export default alt.createStore(class {
+ static displayName = 'ViewerStore'
+
+ static config = {
+ getState(state) {
+ return {
+ selectedData: state.selectedData
+ }
+ }
+ }
+
+ constructor() {
+ this.selectedData = {}
+
+ this.bindActions(DebugActions)
+ }
+
+ selectData(data) {
+ this.selectedData = data
+ console.log(data)
+ }
+})
diff --git a/src/utils/debug/alt.js b/src/utils/debug/alt.js
new file mode 100644
index 00000000..1f88a394
--- /dev/null
+++ b/src/utils/debug/alt.js
@@ -0,0 +1,5 @@
+import Alt from '../../'
+
+const alt = new Alt()
+
+export default alt
diff --git a/test/recorder.js b/test/recorder.js
index b0c37582..52fefd71 100644
--- a/test/recorder.js
+++ b/test/recorder.js
@@ -105,6 +105,7 @@ export default {
'serialize and deserialize events'() {
const recording = recorder.record()
+ actions.a()
actions.a('hello')
actions.b('world')
actions.c('it works')
@@ -131,7 +132,7 @@ export default {
newRecorder.loadEvents(serialized)
- assert.equal(newRecorder.events.length, 3, 'events are loaded')
+ assert.equal(newRecorder.events.length, 4, 'events are loaded')
newRecorder.replay()