diff --git a/src/validate/DataValidator.js b/src/validate/DataValidator.js index 2d613f5..4651fcb 100644 --- a/src/validate/DataValidator.js +++ b/src/validate/DataValidator.js @@ -158,6 +158,18 @@ module.exports = Class( 'DataValidator', }, + /** + * Clear all recorded failures + * + * @return {DataValidator} self + */ + 'public clearFailures'() + { + this._field_monitor.clearFailures(); + return this; + }, + + /** * Populate store with data * diff --git a/src/validate/ValidStateMonitor.js b/src/validate/ValidStateMonitor.js index 9fb6ef5..afe8545 100644 --- a/src/validate/ValidStateMonitor.js +++ b/src/validate/ValidStateMonitor.js @@ -261,7 +261,7 @@ module.exports = Class( 'ValidStateMonitor' ) } // looks like it has been resolved - ( fixed[ name ] = fixed[ name ] || [] )[ fail_i ] = result; + this._fixFailure( fixed, name, fail_i, result ); delete past_fail[ fail_i ]; return true; @@ -313,4 +313,66 @@ module.exports = Class( 'ValidStateMonitor' ) ) ); }, + + + /** + * Mark a failure as fixed + * + * @param {Object} fixed destination object + * @param {string} name fixed field name + * @param {number} index fixed field index + * @param {*} value value that caused the fix + * + * @return {Object} `fixed` argument + */ + 'private _fixFailure'( fixed, name, index, value ) + { + ( fixed[ name ] = fixed[ name ] || [] )[ index ] = value; + return fixed; + }, + + + /** + * Clear all recorded failures + * + * For each recorded failure, a `fix` even is emitted. All failure + * records are then cleared. + * + * Normally the resulting fix object contains the values that triggered + * the fix. Instead, each fixed index will contain `undefined`. + * + * This process is synchronous, and only a single `fix` event is emitted + * after all failures have been cleared. + * + * @return {ValidStateMonitor} self + */ + 'public clearFailures'() + { + let fixed = {}; + + for ( let name in this._failures ) + { + const failure = this._failures[ name ]; + + for ( let cause_i in failure ) + { + const cause = failure[ cause_i ]; + + for ( let cause_i in cause ) + { + let fail_i = cause.getField().getIndex(); + + this._fixFailure( fixed, name, fail_i, undefined ); + } + } + } + + // clear _before_ emitting the fixes (listeners might trigger + // additional failures, for example, or call `#hasFailures`) + this._failures = {}; + + this.emit( 'fix', fixed ); + + return this; + }, } ); diff --git a/test/validate/DataValidatorTest.js b/test/validate/DataValidatorTest.js index f01eb0f..8ff4af9 100644 --- a/test/validate/DataValidatorTest.js +++ b/test/validate/DataValidatorTest.js @@ -213,6 +213,30 @@ describe( 'DataValidator', () => ).to.eventually.be.rejectedWith( expected_e ); } ); } ); + + + describe( '#clearFailures', () => + { + it( 'marks all failures as fixed', () => + { + const bvalidator = createMockBucketValidator(); + const vmonitor = ValidStateMonitor(); + const dep_factory = createMockDependencyFactory(); + + const mock_vmonitor = sinon.mock( vmonitor ); + + const sut = Sut( + bvalidator, vmonitor, dep_factory, createStubStore() + ); + + mock_vmonitor.expects( 'clearFailures' ).once(); + + expect( sut.clearFailures() ) + .to.equal( sut ); + + mock_vmonitor.verify(); + } ); + } ); } ); diff --git a/test/validate/ValidStateMonitorTest.js b/test/validate/ValidStateMonitorTest.js index 52dc272..5c44cb4 100644 --- a/test/validate/ValidStateMonitorTest.js +++ b/test/validate/ValidStateMonitorTest.js @@ -592,6 +592,35 @@ describe( 'ValidStateMonitor', function() } ); } ); } ); + + + describe( '#clearFailures', () => + { + it( 'clears all failures', () => + { + return new Promise( ( accept, reject ) => + { + mkstore( {} ).then( empty => + { + const sut = Sut(); + + return sut + .on( 'fix', fixed => + { + expect( fixed ) + .to.deep.equal( { foo: [ undefined ] } ); + + expect( sut.hasFailures() ).to.be.false; + + accept( true ); + } ) + .update( empty, { foo: mkfail( 'foo', [ 'bar' ] ) } ) + .then( sut => sut.clearFailures() ); + } ) + .catch( e => reject( e ) ); + } ); + } ); + } ); } );