diff --git a/app.js b/app.js index 68dc5c8..f4e96d8 100644 --- a/app.js +++ b/app.js @@ -10,7 +10,9 @@ var env = process.env.NODE_ENV || 'development', Reader = require('./lib/reader').Reader, BaseReaderLoader = require('./lib/reader/loader/base').Loader, JsonReaderLoader = require('./lib/reader/loader/json').Loader, - notifier = require('./lib/notifier'), + Notifier = require('./lib/notifier').Notifier, + BaseNotifierTransport = require('./lib/notifier/transport/base').Transport, + ConsoleNotifierTransport = require('./lib/notifier/transport/console').Transport, ProjectsCollection = require('./lib/project').ProjectsCollection, BuildsCollection = require('./lib/build').BuildsCollection, libLogger = require('./lib/logger'), @@ -60,7 +62,7 @@ app.httpServer.addRequestListener(function(req, res, next) { app.lib = {}; app.lib.BaseReaderLoader = BaseReaderLoader; -app.lib.notifier = notifier; +app.lib.BaseNotifierTransport = BaseNotifierTransport; app.lib.logger = libLogger; var configDefaults = { @@ -210,6 +212,9 @@ Steppy( baseDir: app.config.paths.projects }); + app.notifier = new Notifier({db: db}); + app.notifier.register('console', ConsoleNotifierTransport); + completeUncompletedBuilds(this.slot()); }, function(err) { @@ -222,13 +227,12 @@ Steppy( }); // register other plugins - require('./lib/notifier/console').register(app); _(app.config.plugins).each(function(plugin) { logger.log('Load plugin "%s"', plugin); require(plugin).register(app); }); - notifier.init(app.config.notify, this.slot()); + app.notifier.init(app.config.notify, this.slot()); }, function() { // load projects after all plugins to provide ability for plugins to diff --git a/distributor.js b/distributor.js index 8bacbf1..e7e4a75 100644 --- a/distributor.js +++ b/distributor.js @@ -11,6 +11,7 @@ exports.init = function(app, callback) { var distributor = new Distributor({ nodes: app.config.nodes, projects: app.projects, + notifier: app.notifier, saveBuild: function(build, callback) { Steppy( function() { diff --git a/lib/distributor.js b/lib/distributor.js index 9d244b9..8f70277 100644 --- a/lib/distributor.js +++ b/lib/distributor.js @@ -5,7 +5,6 @@ var Steppy = require('twostep').Steppy, Node = require('./node').Node, EventEmitter = require('events').EventEmitter, inherits = require('util').inherits, - notifier = require('./notifier'), logger = require('./logger')('distributor'); @@ -27,6 +26,7 @@ function Distributor(params) { }; self.projects = params.projects; + self.notifier = params.notifier; self.buildLogLineNumbersHash = {}, self.lastLinesHash = {}; @@ -202,7 +202,7 @@ Distributor.prototype._onBuildComplete = function(build, callback) { Steppy( function() { // notify about build - notifier.send(build); + self.notifier.send(build); // process after build triggers var triggerAfterGroup = this.makeGroup(); diff --git a/lib/notifier/base.js b/lib/notifier/base.js deleted file mode 100644 index c21520f..0000000 --- a/lib/notifier/base.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -function Notifier() { -} - -exports.Notifier = Notifier; - -Notifier.prototype.init = function(params, callback) { - callback(); -}; - -/* - * {Object} params.notifyReson - * {Object} params.build - */ -Notifier.prototype.send = function(params, callback) { - callback(); -}; diff --git a/lib/notifier/console.js b/lib/notifier/console.js deleted file mode 100644 index e4f7cff..0000000 --- a/lib/notifier/console.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -var BaseNotifier = require('./base').Notifier, - inherits = require('util').inherits; - -function Notifier() { -} - -inherits(Notifier, BaseNotifier); - -exports.register = function(app) { - app.lib.notifier.register('console', Notifier); -}; - -Notifier.prototype.send = function(params, callback) { - var build = params.build; - console.log( - 'NOTIFY on %s: build #%s of project %s is %s', - params.notifyReason.strategy, - build.number, - build.project.name, - build.status - ); -}; diff --git a/lib/notifier/index.js b/lib/notifier/index.js index 819b4fb..a456678 100644 --- a/lib/notifier/index.js +++ b/lib/notifier/index.js @@ -2,27 +2,31 @@ var Steppy = require('twostep').Steppy, _ = require('underscore'), - db = require('../../db'), - logger = require('../logger')('notifier'), - BaseNotifier = require('./base').Notifier; + logger = require('../logger')('notifier'); -exports.BaseNotifier = BaseNotifier; +function Notifier(params) { + this.db = params.db; -var constructors = {}, - instances = {}; + this.constructors = {}; + this.instances = {}; +} -exports.register = function(type, constructor) { - constructors[type] = constructor; +exports.Notifier = Notifier; + +Notifier.prototype.register = function(type, constructor) { + this.constructors[type] = constructor; }; -exports.init = function(params, callback) { +Notifier.prototype.init = function(params, callback) { + var self = this; + Steppy( function() { var initGroup = this.makeGroup(); - _(constructors).each(function(Constructor, type) { - instances[type] = new Constructor(); - instances[type].init(params[type], initGroup.slot()); + _(self.constructors).each(function(Constructor, type) { + self.instances[type] = new Constructor(); + self.instances[type].init(params[type], initGroup.slot()); }); }, callback @@ -30,11 +34,13 @@ exports.init = function(params, callback) { }; // Returns previous (by number) build from the same project -exports._getPrevBuild = function(build, callback) { +Notifier.prototype._getPrevBuild = function(build, callback) { + var self = this; + Steppy( function() { // get id of prev build - db.builds.find({ + self.db.builds.find({ start: { projectName: build.project.name, number: build.number - 1 @@ -44,7 +50,7 @@ exports._getPrevBuild = function(build, callback) { }, function(err, builds) { // get prev build by id - db.builds.find({start: {id: builds[0].id}}, this.slot()); + self.db.builds.find({start: {id: builds[0].id}}, this.slot()); }, function(err, builds) { this.pass(builds[0]); @@ -56,12 +62,14 @@ exports._getPrevBuild = function(build, callback) { /* * Check if that's completed build should be notified, then notify */ -exports.send = function(build, callback) { +Notifier.prototype.send = function(build, callback) { callback = callback || function(err) { if (err) { logger.error('Error during send:', err.stack || err); } }; + var self = this; + Steppy( function() { if (!build.completed) { @@ -82,7 +90,7 @@ exports.send = function(build, callback) { build.number > 1 && _(notify.on).intersection(['change']).length ) { - exports._getPrevBuild(build, this.slot()); + self._getPrevBuild(build, this.slot()); } }, function(err, notify, prevBuild) { @@ -106,10 +114,10 @@ exports.send = function(build, callback) { _(notify.to).each(function(recipients, type) { logger.log( 'Notify about ' + build.project.name + ' build #' + - build.number+ ' "' + strategy + '" via ' + type + build.number + ' "' + strategy + '" via ' + type ); - if (type in instances) { - instances[type].send({ + if (type in self.instances) { + self.instances[type].send({ build: build, notifyReason: {strategy: strategy} }, notifyGroup.slot()); diff --git a/lib/notifier/transport/base.js b/lib/notifier/transport/base.js new file mode 100644 index 0000000..d2b2e1f --- /dev/null +++ b/lib/notifier/transport/base.js @@ -0,0 +1,18 @@ +'use strict'; + +function Transport() { +} + +exports.Transport = Transport; + +Transport.prototype.init = function(params, callback) { + callback(); +}; + +/* + * {Object} params.notifyReson + * {Object} params.build + */ +Transport.prototype.send = function(params, callback) { + callback(); +}; diff --git a/lib/notifier/transport/console.js b/lib/notifier/transport/console.js new file mode 100644 index 0000000..f7c652a --- /dev/null +++ b/lib/notifier/transport/console.js @@ -0,0 +1,22 @@ +'use strict'; + +var BaseTransport = require('./base').Transport, + inherits = require('util').inherits; + +function Transport() { +} + +inherits(Transport, BaseTransport); + +exports.Transport = Transport; + +Transport.prototype.send = function(params, callback) { + var build = params.build; + console.log( + 'NOTIFY on %s: build #%s of project %s is %s', + params.notifyReason.strategy, + build.number, + build.project.name, + build.status + ); +}; diff --git a/test/distributor/blocking.js b/test/distributor/blocking.js index 756ca69..3b009f0 100644 --- a/test/distributor/blocking.js +++ b/test/distributor/blocking.js @@ -1,10 +1,8 @@ 'use strict'; -var Distributor = require('../../lib/distributor').Distributor, - expect = require('expect.js'), +var expect = require('expect.js'), sinon = require('sinon'), - createNodeMock = require('./helpers').createNodeMock, - createProjectsMock = require('./helpers').createProjectsMock, + helpers = require('./helpers'), Steppy = require('twostep').Steppy; @@ -18,15 +16,11 @@ describe('Distributor blocking with max 2 executors count', function() { var nodes = [{type: 'local', maxExecutorsCount: 2}]; - before(function() { - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - sinon.stub().callsArgAsync(1) - )); - }); - var itRunParallelProjects = function() { it('distributor should be created without errors', function() { - distributor = new Distributor({projects: projects, nodes: nodes}); + distributor = helpers.createDistributor({ + projects: projects, nodes: nodes + }); updateBuildSpy = sinon.spy(distributor, '_updateBuild'); }); @@ -66,7 +60,9 @@ describe('Distributor blocking with max 2 executors count', function() { var itRunSequentialProjects = function() { it('distributor should be created without errors', function() { - distributor = new Distributor({projects: projects, nodes: nodes}); + distributor = helpers.createDistributor({ + projects: projects, nodes: nodes + }); updateBuildSpy = sinon.spy(distributor, '_updateBuild'); }); @@ -120,11 +116,11 @@ describe('Distributor blocking with max 2 executors count', function() { describe('should run 2 non-blocking projects in parallel', function() { before(function() { - projects = createProjectsMock([{ + projects = [{ name: 'project1', }, { name: 'project2' - }]); + }]; }); itRunParallelProjects(); @@ -132,12 +128,12 @@ describe('Distributor blocking with max 2 executors count', function() { describe('should run project1, then 2, when 2 blocked by 1', function() { before(function() { - projects = createProjectsMock([{ + projects = [{ name: 'project1', }, { name: 'project2', blockedBy: ['project1'] - }]); + }]; }); itRunSequentialProjects(); @@ -145,12 +141,12 @@ describe('Distributor blocking with max 2 executors count', function() { describe('should run project1, then 2, when 1 blocks 2', function() { before(function() { - projects = createProjectsMock([{ + projects = [{ name: 'project1', blocks: ['project2'] }, { name: 'project2' - }]); + }]; }); itRunSequentialProjects(); @@ -160,7 +156,7 @@ describe('Distributor blocking with max 2 executors count', function() { 'should run 1, 2 in parallel, when 1 block 3, 2 blocked by 3', function() { before(function() { - projects = createProjectsMock([{ + projects = [{ name: 'project1', blocks: ['project3'] }, { @@ -168,15 +164,10 @@ describe('Distributor blocking with max 2 executors count', function() { blockedBy: ['project3'] }, { name: 'project3' - }]); + }]; }); itRunParallelProjects(); } ); - - after(function() { - Distributor.prototype._createNode.restore(); - }); - }); diff --git a/test/distributor/helpers.js b/test/distributor/helpers.js index 39f0734..2da42c6 100644 --- a/test/distributor/helpers.js +++ b/test/distributor/helpers.js @@ -1,11 +1,15 @@ 'use strict'; -var Node = require('../../lib/node').Node, +var _ = require('underscore'), + sinon = require('sinon'), + Node = require('../../lib/node').Node, EventEmitter = require('events').EventEmitter, - ProjectsCollection = require('../../lib/project').ProjectsCollection; + ProjectsCollection = require('../../lib/project').ProjectsCollection, + Distributor = require('../../lib/distributor').Distributor, + Notifier = require('../../lib/notifier').Notifier; -exports.createNodeMock = function(executorRun) { +var createNode = function(executorRun) { return function(params) { var node = new Node(params); node._createExecutor = function(project) { @@ -18,8 +22,40 @@ exports.createNodeMock = function(executorRun) { }; }; -exports.createProjectsMock = function(configs) { +var createProjects = function(configs) { var projects = new ProjectsCollection({}); projects.configs = configs; return projects; }; + +exports.createDistributor = function(params) { + var mockNode = _(params).has('mockNode') ? params.mockNode : true; + + var distributorParams = _(params).clone(); + + if (mockNode) { + var executorRun = ( + distributorParams.executorRun || sinon.stub().callsArgAsync(1) + ); + // patch method which will be called at constructor + sinon.stub(Distributor.prototype, '_createNode', createNode( + executorRun + )); + delete distributorParams.executorRun; + } + + if (distributorParams.projects) { + distributorParams.projects = createProjects(distributorParams.projects); + } + _(distributorParams).defaults({ + notifier: new Notifier({}) + }); + + var distributor = new Distributor(distributorParams); + + if (mockNode) { + Distributor.prototype._createNode.restore(); + } + + return distributor; +}; diff --git a/test/distributor/main.js b/test/distributor/main.js index 7c03656..1d1a20a 100644 --- a/test/distributor/main.js +++ b/test/distributor/main.js @@ -1,15 +1,13 @@ 'use strict'; -var Distributor = require('../../lib/distributor').Distributor, - expect = require('expect.js'), +var expect = require('expect.js'), sinon = require('sinon'), - createNodeMock = require('./helpers').createNodeMock, - createProjectsMock = require('./helpers').createProjectsMock; + helpers = require('./helpers'); describe('Distributor main', function() { var distributor, - projects = createProjectsMock([{name: 'project1'}]); + projects = [{name: 'project1'}]; var expectUpdateBuild = function(distributor, build, number, conditionsHash) { var conditions = conditionsHash[number]; @@ -21,16 +19,10 @@ describe('Distributor main', function() { }; describe('with success project', function() { - before(function() { - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - sinon.stub().callsArgAsync(1) - )); - }); - var updateBuildSpy; it('instance should be created without errors', function() { - distributor = new Distributor({ + distributor = helpers.createDistributor({ projects: projects, nodes: [{type: 'local', maxExecutorsCount: 1}] }); @@ -74,25 +66,19 @@ describe('Distributor main', function() { it('update build called 3 times in total', function() { expect(updateBuildSpy.callCount).equal(3); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); describe('with fail project', function() { - before(function() { - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - sinon.stub().callsArgWithAsync(1, new Error('Some error')) - )); - }); - var updateBuildSpy; it('instance should be created without errors', function() { - distributor = new Distributor({ + distributor = helpers.createDistributor({ projects: projects, - nodes: [{type: 'local', maxExecutorsCount: 1}] + nodes: [{type: 'local', maxExecutorsCount: 1}], + executorRun: sinon.stub().callsArgWithAsync( + 1, + new Error('Some error') + ) }); updateBuildSpy = sinon.spy(distributor, '_updateBuild'); }); @@ -124,19 +110,9 @@ describe('Distributor main', function() { it('update build called 3 times in total', function() { expect(updateBuildSpy.callCount).equal(3); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); describe('with success project and build cancel', function() { - before(function() { - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - sinon.stub().callsArgAsync(1) - )); - }); - var distributorParams = { projects: projects, nodes: [{type: 'local', maxExecutorsCount: 1}], @@ -146,12 +122,12 @@ describe('Distributor main', function() { } }; - describe('when cancel queued buid', function() { + describe('when cancel queued bulid', function() { var updateBuildSpy; var cancelError; it('instance should be created without errors', function() { - distributor = new Distributor(distributorParams); + distributor = helpers.createDistributor(distributorParams); var originalRunNext = distributor._runNext; distributor._runNext = function() { @@ -194,7 +170,7 @@ describe('Distributor main', function() { var cancelError; it('instance should be created without errors', function() { - distributor = new Distributor(distributorParams); + distributor = helpers.createDistributor(distributorParams); var originalRunNext = distributor._runNext; distributor._runNext = function() { @@ -219,9 +195,5 @@ describe('Distributor main', function() { ); }); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); }); diff --git a/test/distributor/runSelfAfterCatchRev.js b/test/distributor/runSelfAfterCatchRev.js index fdf8484..e765ece 100644 --- a/test/distributor/runSelfAfterCatchRev.js +++ b/test/distributor/runSelfAfterCatchRev.js @@ -1,8 +1,7 @@ -var Distributor = require('../../lib/distributor').Distributor, - expect = require('expect.js'), +var expect = require('expect.js'), sinon = require('sinon'), helpers = require('../helpers'), - createProjectsMock = require('./helpers').createProjectsMock, + distributorHelpers = require('./helpers'), path = require('path'); describe('Distributor run self after catch', function() { @@ -16,8 +15,8 @@ describe('Distributor run self after catch', function() { before(function(done) { helpers.removeDirIfExists(workspacePath, done); - distributor = new Distributor({ - projects: createProjectsMock([{ + distributor = distributorHelpers.createDistributor({ + projects: [{ name: 'project1', dir: __dirname, scm: helpers.repository.scm, @@ -25,8 +24,9 @@ describe('Distributor run self after catch', function() { {type: 'shell', cmd: 'echo 1'} ], catchRev: {comment: /.*/} - }]), - nodes: nodes + }], + nodes: nodes, + mockNode: false }); var createExecutor = distributor.nodes[0]._createExecutor; diff --git a/test/distributor/triggerAfter.js b/test/distributor/triggerAfter.js index 2461e5f..333d394 100644 --- a/test/distributor/triggerAfter.js +++ b/test/distributor/triggerAfter.js @@ -1,10 +1,8 @@ 'use strict'; -var Distributor = require('../../lib/distributor').Distributor, - expect = require('expect.js'), +var expect = require('expect.js'), sinon = require('sinon'), - createNodeMock = require('./helpers').createNodeMock, - createProjectsMock = require('./helpers').createProjectsMock; + helpers = require('./helpers'); describe('Distributor trigger after', function() { @@ -14,22 +12,23 @@ describe('Distributor trigger after', function() { describe('done when project is done', function() { before(function() { - projects = createProjectsMock([{ + projects = [{ name: 'project1', trigger: { after: [{status: 'done', project: 'project2'}] } }, { name: 'project2' - }]); + }]; executorRunSpy = sinon.stub().callsArgAsync(1); - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - executorRunSpy - )); }); it('distributor should be created without errors', function() { - distributor = new Distributor({projects: projects, nodes: nodes}); + distributor = helpers.createDistributor({ + projects: projects, + nodes: nodes, + executorRun: executorRunSpy + }); }); it('should run without errors', function(done) { @@ -40,24 +39,16 @@ describe('Distributor trigger after', function() { }); it('should run project1 at first call', function() { - expect(executorRunSpy.getCall(0).thisValue.project).eql( - projects.get('project1') - ); + expect(executorRunSpy.getCall(0).thisValue.project).eql(projects[0]); }); it('should run project2 at second call', function() { - expect(executorRunSpy.getCall(1).thisValue.project).eql( - projects.get('project2') - ); + expect(executorRunSpy.getCall(1).thisValue.project).eql(projects[1]); }); it('should run totally 2 times', function() { expect(executorRunSpy.callCount).equal(2); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); describe('done when project is error', function() { @@ -65,13 +56,14 @@ describe('Distributor trigger after', function() { executorRunSpy = sinon.stub().callsArgWithAsync(1, new Error( 'Some error' )); - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - executorRunSpy - )); }); it('distributor should be created without errors', function() { - distributor = new Distributor({projects: projects, nodes: nodes}); + distributor = helpers.createDistributor({ + projects: projects, + nodes: nodes, + executorRun: executorRunSpy + }); }); it('should run without errors', function(done) { @@ -82,38 +74,33 @@ describe('Distributor trigger after', function() { }); it('should run project1 at first call', function() { - expect(executorRunSpy.getCall(0).thisValue.project).eql( - projects.get('project1') - ); + expect(executorRunSpy.getCall(0).thisValue.project).eql(projects[0]); }); it('should run totally 1 time', function() { expect(executorRunSpy.callCount).equal(1); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); describe('status is not set when project is done', function() { before(function() { - projects = createProjectsMock([{ + projects = [{ name: 'project1', trigger: { after: [{project: 'project2'}] } }, { name: 'project2' - }]); + }]; executorRunSpy = sinon.stub().callsArgAsync(1); - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - executorRunSpy - )); }); it('distributor should be created without errors', function() { - distributor = new Distributor({projects: projects, nodes: nodes}); + distributor = helpers.createDistributor({ + projects: projects, + nodes: nodes, + executorRun: executorRunSpy + }); }); it('should run without errors', function(done) { @@ -124,24 +111,16 @@ describe('Distributor trigger after', function() { }); it('should run project1 at first call', function() { - expect(executorRunSpy.getCall(0).thisValue.project).eql( - projects.get('project1') - ); + expect(executorRunSpy.getCall(0).thisValue.project).eql(projects[0]); }); it('should run project2 at second call', function() { - expect(executorRunSpy.getCall(1).thisValue.project).eql( - projects.get('project2') - ); + expect(executorRunSpy.getCall(1).thisValue.project).eql(projects[1]); }); it('should run totally 2 times', function() { expect(executorRunSpy.callCount).equal(2); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); describe('status is not set when project is error', function() { @@ -149,13 +128,14 @@ describe('Distributor trigger after', function() { executorRunSpy = sinon.stub().callsArgWithAsync(1, new Error( 'Some error' )); - sinon.stub(Distributor.prototype, '_createNode', createNodeMock( - executorRunSpy - )); }); it('distributor should be created without errors', function() { - distributor = new Distributor({projects: projects, nodes: nodes}); + distributor = helpers.createDistributor({ + projects: projects, + nodes: nodes, + executorRun: executorRunSpy + }); }); it('should run without errors', function(done) { @@ -166,24 +146,16 @@ describe('Distributor trigger after', function() { }); it('should run project1 at first call', function() { - expect(executorRunSpy.getCall(0).thisValue.project).eql( - projects.get('project1') - ); + expect(executorRunSpy.getCall(0).thisValue.project).eql(projects[0]); }); it('should run project2 at second call', function() { - expect(executorRunSpy.getCall(1).thisValue.project).eql( - projects.get('project2') - ); + expect(executorRunSpy.getCall(1).thisValue.project).eql(projects[1]); }); it('should run totally 2 times', function() { expect(executorRunSpy.callCount).equal(2); }); - - after(function() { - Distributor.prototype._createNode.restore(); - }); }); }); diff --git a/test/notifier.js b/test/notifier.js index 265a761..5ea0aef 100644 --- a/test/notifier.js +++ b/test/notifier.js @@ -1,6 +1,6 @@ 'use strict'; -var notifier = require('../lib/notifier'), +var Notifier = require('../lib/notifier').Notifier, expect = require('expect.js'), sinon = require('sinon'), _ = require('underscore'); @@ -8,16 +8,18 @@ var notifier = require('../lib/notifier'), describe('notifier module', function() { - function TestNotifier() { - } - TestNotifier.prototype.init = sinon.stub().callsArg(1); - TestNotifier.prototype.send = sinon.stub().callsArg(1); + var notifier = new Notifier({}); - var sendSpy = TestNotifier.prototype.send; + function TestTransport() { + } + TestTransport.prototype.init = sinon.stub().callsArg(1); + TestTransport.prototype.send = sinon.stub().callsArg(1); + + var sendSpy = TestTransport.prototype.send; describe('test notifier', function() { it('should be rigestered', function() { - notifier.register('test', TestNotifier); + notifier.register('test', TestTransport); }); it('should be intialized without errors', function(done) { @@ -25,7 +27,7 @@ describe('notifier module', function() { }); it('init method should be called once during init', function() { - expect(TestNotifier.prototype.init.calledOnce).equal(true); + expect(TestTransport.prototype.init.calledOnce).equal(true); }); });