|
|
@ -21,13 +21,12 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
mock.loggerOutput.val = ''; |
|
|
|
mock.loggerOutput.val = ''; |
|
|
|
solcoverConfig = {}; |
|
|
|
solcoverConfig = {}; |
|
|
|
truffleConfig = mock.getDefaultTruffleConfig(); |
|
|
|
truffleConfig = mock.getDefaultTruffleConfig(); |
|
|
|
|
|
|
|
verify.cleanInitialState(); |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
afterEach(() => mock.clean()); |
|
|
|
afterEach(() => mock.clean()); |
|
|
|
|
|
|
|
|
|
|
|
it('simple contract', async function(){ |
|
|
|
it('simple contract', async function(){ |
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Simple', 'simple.js', solcoverConfig); |
|
|
|
mock.install('Simple', 'simple.js', solcoverConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
@ -36,21 +35,18 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const path = Object.keys(output)[0]; |
|
|
|
const path = Object.keys(output)[0]; |
|
|
|
|
|
|
|
|
|
|
|
assert(output[path].fnMap['1'].name === 'test', 'coverage.json missing "test"'); |
|
|
|
assert( |
|
|
|
assert(output[path].fnMap['2'].name === 'getX', 'coverage.json missing "getX"'); |
|
|
|
output[path].fnMap['1'].name === 'test', |
|
|
|
}); |
|
|
|
'coverage.json missing "test"' |
|
|
|
|
|
|
|
); |
|
|
|
// Truffle test asserts balance is 777 ether
|
|
|
|
|
|
|
|
it('config with providerOptions', async function() { |
|
|
|
|
|
|
|
solcoverConfig.providerOptions = { default_balance_ether: 777 } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Simple', 'testrpc-options.js', solcoverConfig); |
|
|
|
assert( |
|
|
|
await plugin(truffleConfig); |
|
|
|
output[path].fnMap['2'].name === 'getX', |
|
|
|
|
|
|
|
'coverage.json missing "getX"' |
|
|
|
|
|
|
|
); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('large contract with many unbracketed statements (time check)', async function() { |
|
|
|
it('with many unbracketed statements (time check)', async function() { |
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
truffleConfig.compilers.solc.version = "0.4.24"; |
|
|
|
truffleConfig.compilers.solc.version = "0.4.24"; |
|
|
|
|
|
|
|
|
|
|
|
mock.install('Oraclize', 'oraclize.js', solcoverConfig, truffleConfig, true); |
|
|
|
mock.install('Oraclize', 'oraclize.js', solcoverConfig, truffleConfig, true); |
|
|
@ -59,8 +55,7 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
|
|
|
|
|
|
|
|
// This project has three contract suites and uses .deployed() instances which
|
|
|
|
// This project has three contract suites and uses .deployed() instances which
|
|
|
|
// depend on truffle's migratons and the inter-test evm_revert / evm_snapshot mechanism.
|
|
|
|
// depend on truffle's migratons and the inter-test evm_revert / evm_snapshot mechanism.
|
|
|
|
it('project evm_reverts repeatedly', async function() { |
|
|
|
it('with multiple migrations (evm_reverts repeatedly)', async function() { |
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
mock.installFullProject('multiple-migrations'); |
|
|
|
mock.installFullProject('multiple-migrations'); |
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
@ -82,84 +77,17 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
verify.lineCoverage(expected); |
|
|
|
verify.lineCoverage(expected); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// This project has [ @skipForCoverage ] tags in the test descriptions
|
|
|
|
it('with relative path solidity imports', async function() { |
|
|
|
// at selected 'contract' and 'it' blocks.
|
|
|
|
mock.installFullProject('import-paths'); |
|
|
|
it('uses solcoverjs mocha options', async function() { |
|
|
|
|
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
solcoverConfig.mocha = { |
|
|
|
|
|
|
|
grep: '@skipForCoverage', |
|
|
|
|
|
|
|
invert: true, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
solcoverConfig.silent = process.env.SILENT ? true : false, |
|
|
|
|
|
|
|
solcoverConfig.istanbulReporter = ['json-summary', 'text'] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.installFullProject('multiple-migrations', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expected = [ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractA.sol'), |
|
|
|
|
|
|
|
pct: 0 |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractB.sol'), |
|
|
|
|
|
|
|
pct: 0, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractC.sol'), |
|
|
|
|
|
|
|
pct: 100, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify.lineCoverage(expected); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('skips a folder', async function() { |
|
|
|
|
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
mock.installFullProject('skipping'); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expected = [{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractA.sol'), |
|
|
|
|
|
|
|
pct: 100 |
|
|
|
|
|
|
|
}]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const missing = [{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'skipped-folder/ContractB.sol'), |
|
|
|
|
|
|
|
}]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify.lineCoverage(expected); |
|
|
|
|
|
|
|
verify.coverageMissing(missing); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('uses "onServerReady", "onTestsComplete", "onIstanbulComplete"', async function() { |
|
|
|
|
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
truffleConfig.logger = mock.testLogger; |
|
|
|
|
|
|
|
mock.installFullProject('test-files'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
assert( |
|
|
|
|
|
|
|
mock.loggerOutput.val.includes('running onServerReady') && |
|
|
|
|
|
|
|
mock.loggerOutput.val.includes('running onTestsComplete') && |
|
|
|
|
|
|
|
mock.loggerOutput.val.includes('running onIstanbulComplete'), |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`Should run "on" hooks : ${mock.loggerOutput.val}` |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('project with relative path solidity imports', async function() { |
|
|
|
it('uses libraries', async function() { |
|
|
|
verify.cleanInitialState(); |
|
|
|
mock.installFullProject('libraries'); |
|
|
|
mock.installFullProject('import-paths'); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('project contains native solidity tests', async function(){ |
|
|
|
it('uses native solidity tests', async function(){ |
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Simple', 'TestSimple.sol', solcoverConfig); |
|
|
|
mock.install('Simple', 'TestSimple.sol', solcoverConfig); |
|
|
|
|
|
|
|
|
|
|
|
truffleConfig.logger = mock.testLogger; |
|
|
|
truffleConfig.logger = mock.testLogger; |
|
|
@ -171,63 +99,58 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
); |
|
|
|
); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('contract only uses ".call"', async function(){ |
|
|
|
it('uses inheritance', async function() { |
|
|
|
verify.cleanInitialState(); |
|
|
|
mock.installDouble( |
|
|
|
|
|
|
|
['Proxy', 'Owned'], |
|
|
|
|
|
|
|
'inheritance.js', |
|
|
|
|
|
|
|
solcoverConfig |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
mock.install('OnlyCall', 'only-call.js', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const path = Object.keys(output)[0]; |
|
|
|
const ownedPath = Object.keys(output)[0]; |
|
|
|
assert(output[path].fnMap['1'].name === 'addTwo', 'cov should map "addTwo"'); |
|
|
|
const proxyPath = Object.keys(output)[1]; |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('contract sends / transfers to instrumented fallback', async function(){ |
|
|
|
|
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Wallet', 'wallet.js', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
assert( |
|
|
|
|
|
|
|
output[ownedPath].fnMap['1'].name === 'constructor', |
|
|
|
|
|
|
|
'"constructor" not covered' |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
assert( |
|
|
|
const path = Object.keys(output)[0]; |
|
|
|
output[proxyPath].fnMap['1'].name === 'isOwner', |
|
|
|
assert(output[path].fnMap['1'].name === 'transferPayment', 'cov should map "transferPayment"'); |
|
|
|
'"isOwner" not covered' |
|
|
|
|
|
|
|
); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('contracts are skipped', async function() { |
|
|
|
it('only uses ".call"', async function(){ |
|
|
|
verify.cleanInitialState(); |
|
|
|
mock.install('OnlyCall', 'only-call.js', solcoverConfig); |
|
|
|
|
|
|
|
|
|
|
|
solcoverConfig.skipFiles = ['Owned.sol']; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const firstKey = Object.keys(output)[0]; |
|
|
|
const path = Object.keys(output)[0]; |
|
|
|
|
|
|
|
assert( |
|
|
|
assert(Object.keys(output).length === 1, 'Wrong # of contracts covered'); |
|
|
|
output[path].fnMap['1'].name === 'addTwo', |
|
|
|
assert(firstKey.substr(firstKey.length - 9) === 'Proxy.sol', 'Wrong contract covered'); |
|
|
|
'cov should map "addTwo"' |
|
|
|
|
|
|
|
); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('contract uses inheritance', async function() { |
|
|
|
it('sends / transfers to instrumented fallback', async function(){ |
|
|
|
verify.cleanInitialState(); |
|
|
|
mock.install('Wallet', 'wallet.js', solcoverConfig); |
|
|
|
|
|
|
|
|
|
|
|
mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
const ownedPath = Object.keys(output)[0]; |
|
|
|
const path = Object.keys(output)[0]; |
|
|
|
const proxyPath = Object.keys(output)[1]; |
|
|
|
assert( |
|
|
|
|
|
|
|
output[path].fnMap['1'].name === 'transferPayment', |
|
|
|
assert(output[ownedPath].fnMap['1'].name === 'constructor', '"constructor" not covered'); |
|
|
|
'cov should map "transferPayment"' |
|
|
|
assert(output[proxyPath].fnMap['1'].name === 'isOwner', '"isOwner" not covered'); |
|
|
|
); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Truffle test asserts deployment cost is greater than 20,000,000 gas
|
|
|
|
// Truffle test asserts deployment cost is greater than 20,000,000 gas
|
|
|
@ -237,9 +160,7 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Simple.sol with a failing assertion in a truffle test
|
|
|
|
// Simple.sol with a failing assertion in a truffle test
|
|
|
|
it('truffle tests failing', async function() { |
|
|
|
it('unit tests failing', async function() { |
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Simple', 'truffle-test-fail.js', solcoverConfig); |
|
|
|
mock.install('Simple', 'truffle-test-fail.js', solcoverConfig); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
@ -259,11 +180,8 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it('uses the fallback server', async function(){ |
|
|
|
it('uses the fallback server', async function(){ |
|
|
|
verify.cleanInitialState(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
truffleConfig.logger = mock.testLogger; |
|
|
|
truffleConfig.logger = mock.testLogger; |
|
|
|
|
|
|
|
solcoverConfig.forceBackupServer = true; |
|
|
|
solcoverConfig = { forceBackupServer: true } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Simple', 'simple.js', solcoverConfig); |
|
|
|
mock.install('Simple', 'simple.js', solcoverConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
|
await plugin(truffleConfig); |
|
|
@ -272,6 +190,104 @@ describe('Truffle Plugin: standard use cases', function() { |
|
|
|
mock.loggerOutput.val.includes("Using ganache-core-sc"), |
|
|
|
mock.loggerOutput.val.includes("Using ganache-core-sc"), |
|
|
|
`Should notify about backup server module: ${mock.loggerOutput.val}` |
|
|
|
`Should notify about backup server module: ${mock.loggerOutput.val}` |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This project has [ @skipForCoverage ] tags in the test descriptions
|
|
|
|
|
|
|
|
// at selected 'contract' and 'it' blocks.
|
|
|
|
|
|
|
|
it('config: mocha options', async function() { |
|
|
|
|
|
|
|
solcoverConfig.mocha = { |
|
|
|
|
|
|
|
grep: '@skipForCoverage', |
|
|
|
|
|
|
|
invert: true, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
solcoverConfig.silent = process.env.SILENT ? true : false, |
|
|
|
|
|
|
|
solcoverConfig.istanbulReporter = ['json-summary', 'text'] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.installFullProject('multiple-migrations', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expected = [ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractA.sol'), |
|
|
|
|
|
|
|
pct: 0 |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractB.sol'), |
|
|
|
|
|
|
|
pct: 0, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractC.sol'), |
|
|
|
|
|
|
|
pct: 100, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify.lineCoverage(expected); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Truffle test asserts balance is 777 ether
|
|
|
|
|
|
|
|
it('config: providerOptions', async function() { |
|
|
|
|
|
|
|
solcoverConfig.providerOptions = { default_balance_ether: 777 } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.install('Simple', 'testrpc-options.js', solcoverConfig); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('config: skipped file', async function() { |
|
|
|
|
|
|
|
solcoverConfig.skipFiles = ['Owned.sol']; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mock.installDouble( |
|
|
|
|
|
|
|
['Proxy', 'Owned'], |
|
|
|
|
|
|
|
'inheritance.js', |
|
|
|
|
|
|
|
solcoverConfig |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify.coverageGenerated(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const output = mock.getOutput(truffleConfig); |
|
|
|
|
|
|
|
const firstKey = Object.keys(output)[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert( |
|
|
|
|
|
|
|
Object.keys(output).length === 1, |
|
|
|
|
|
|
|
'Wrong # of contracts covered' |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert( |
|
|
|
|
|
|
|
firstKey.substr(firstKey.length - 9) === 'Proxy.sol', |
|
|
|
|
|
|
|
'Wrong contract covered' |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('config: skipped folder', async function() { |
|
|
|
|
|
|
|
mock.installFullProject('skipping'); |
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expected = [{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'ContractA.sol'), |
|
|
|
|
|
|
|
pct: 100 |
|
|
|
|
|
|
|
}]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const missing = [{ |
|
|
|
|
|
|
|
file: mock.pathToContract(truffleConfig, 'skipped-folder/ContractB.sol'), |
|
|
|
|
|
|
|
}]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify.lineCoverage(expected); |
|
|
|
|
|
|
|
verify.coverageMissing(missing); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('config: "onServerReady", "onTestsComplete", ...', async function() { |
|
|
|
|
|
|
|
truffleConfig.logger = mock.testLogger; |
|
|
|
|
|
|
|
mock.installFullProject('test-files'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await plugin(truffleConfig); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert( |
|
|
|
|
|
|
|
mock.loggerOutput.val.includes('running onServerReady') && |
|
|
|
|
|
|
|
mock.loggerOutput.val.includes('running onTestsComplete') && |
|
|
|
|
|
|
|
mock.loggerOutput.val.includes('running onIstanbulComplete'), |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`Should run "on" hooks : ${mock.loggerOutput.val}` |
|
|
|
|
|
|
|
); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}) |
|
|
|
}) |
|
|
|