ProjectRootDocManagerTests.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. no-useless-escape,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const chai = require('chai')
  15. const should = chai.should()
  16. const { expect } = chai
  17. const sinon = require('sinon')
  18. const modulePath =
  19. '../../../../app/src/Features/Project/ProjectRootDocManager.js'
  20. const SandboxedModule = require('sandboxed-module')
  21. describe('ProjectRootDocManager', function() {
  22. beforeEach(function() {
  23. this.project_id = 'project-123'
  24. this.docPaths = {
  25. 'doc-id-1': '/chapter1.tex',
  26. 'doc-id-2': '/main.tex',
  27. 'doc-id-3': '/nested/chapter1a.tex',
  28. 'doc-id-4': '/nested/chapter1b.tex'
  29. }
  30. this.sl_req_id = 'sl-req-id-123'
  31. this.callback = sinon.stub()
  32. this.globbyFiles = ['a.tex', 'b.tex', 'main.tex']
  33. this.globby = sinon.stub().returns(
  34. new Promise(resolve => {
  35. return resolve(this.globbyFiles)
  36. })
  37. )
  38. this.fs = {
  39. readFile: sinon.stub().callsArgWith(2, new Error('file not found')),
  40. stat: sinon.stub().callsArgWith(1, null, { size: 100 })
  41. }
  42. return (this.ProjectRootDocManager = SandboxedModule.require(modulePath, {
  43. globals: {
  44. console: console
  45. },
  46. requires: {
  47. './ProjectEntityHandler': (this.ProjectEntityHandler = {}),
  48. './ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
  49. './ProjectGetter': (this.ProjectGetter = {}),
  50. globby: this.globby,
  51. fs: this.fs
  52. }
  53. }))
  54. })
  55. describe('setRootDocAutomatically', function() {
  56. describe('when there is a suitable root doc', function() {
  57. beforeEach(function(done) {
  58. this.docs = {
  59. '/chapter1.tex': {
  60. _id: 'doc-id-1',
  61. lines: [
  62. 'something else',
  63. '\\begin{document}',
  64. 'Hello world',
  65. '\\end{document}'
  66. ]
  67. },
  68. '/main.tex': {
  69. _id: 'doc-id-2',
  70. lines: [
  71. 'different line',
  72. '\\documentclass{article}',
  73. '\\input{chapter1}'
  74. ]
  75. },
  76. '/nested/chapter1a.tex': {
  77. _id: 'doc-id-3',
  78. lines: ['Hello world']
  79. },
  80. '/nested/chapter1b.tex': {
  81. _id: 'doc-id-4',
  82. lines: ['Hello world']
  83. }
  84. }
  85. this.ProjectEntityHandler.getAllDocs = sinon
  86. .stub()
  87. .callsArgWith(1, null, this.docs)
  88. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  89. .stub()
  90. .callsArgWith(2)
  91. return this.ProjectRootDocManager.setRootDocAutomatically(
  92. this.project_id,
  93. done
  94. )
  95. })
  96. it('should check the docs of the project', function() {
  97. return this.ProjectEntityHandler.getAllDocs
  98. .calledWith(this.project_id)
  99. .should.equal(true)
  100. })
  101. it('should set the root doc to the doc containing a documentclass', function() {
  102. return this.ProjectEntityUpdateHandler.setRootDoc
  103. .calledWith(this.project_id, 'doc-id-2')
  104. .should.equal(true)
  105. })
  106. })
  107. describe('when the root doc is an Rtex file', function() {
  108. beforeEach(function() {
  109. this.docs = {
  110. '/chapter1.tex': {
  111. _id: 'doc-id-1',
  112. lines: ['\\begin{document}', 'Hello world', '\\end{document}']
  113. },
  114. '/main.Rtex': {
  115. _id: 'doc-id-2',
  116. lines: ['\\documentclass{article}', '\\input{chapter1}']
  117. }
  118. }
  119. this.ProjectEntityHandler.getAllDocs = sinon
  120. .stub()
  121. .callsArgWith(1, null, this.docs)
  122. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  123. .stub()
  124. .callsArgWith(2)
  125. return this.ProjectRootDocManager.setRootDocAutomatically(
  126. this.project_id,
  127. this.callback
  128. )
  129. })
  130. it('should set the root doc to the doc containing a documentclass', function() {
  131. return this.ProjectEntityUpdateHandler.setRootDoc
  132. .calledWith(this.project_id, 'doc-id-2')
  133. .should.equal(true)
  134. })
  135. })
  136. describe('when there is no suitable root doc', function() {
  137. beforeEach(function(done) {
  138. this.docs = {
  139. '/chapter1.tex': {
  140. _id: 'doc-id-1',
  141. lines: ['\\begin{document}', 'Hello world', '\\end{document}']
  142. },
  143. '/style.bst': {
  144. _id: 'doc-id-2',
  145. lines: ['%Example: \\documentclass{article}']
  146. }
  147. }
  148. this.ProjectEntityHandler.getAllDocs = sinon
  149. .stub()
  150. .callsArgWith(1, null, this.docs)
  151. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  152. .stub()
  153. .callsArgWith(2)
  154. return this.ProjectRootDocManager.setRootDocAutomatically(
  155. this.project_id,
  156. done
  157. )
  158. })
  159. it('should not set the root doc to the doc containing a documentclass', function() {
  160. return this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(
  161. false
  162. )
  163. })
  164. })
  165. })
  166. describe('findRootDocFileFromDirectory', function() {
  167. beforeEach(function() {
  168. this.fs.readFile
  169. .withArgs('/foo/a.tex')
  170. .callsArgWith(2, null, 'Hello World!')
  171. this.fs.readFile
  172. .withArgs('/foo/b.tex')
  173. .callsArgWith(2, null, "I'm a little teapot, get me out of here.")
  174. this.fs.readFile
  175. .withArgs('/foo/main.tex')
  176. .callsArgWith(2, null, "Help, I'm trapped in a unit testing factory")
  177. this.fs.readFile
  178. .withArgs('/foo/c.tex')
  179. .callsArgWith(2, null, 'Tomato, tomahto.')
  180. this.fs.readFile
  181. .withArgs('/foo/a/a.tex')
  182. .callsArgWith(2, null, 'Potato? Potahto. Potootee!')
  183. return (this.documentclassContent = '% test\n\\documentclass\n% test')
  184. })
  185. describe('when there is a file in a subfolder', function() {
  186. beforeEach(function() {
  187. // have to splice globbyFiles weirdly because of the way the stubbed globby method handles references
  188. return this.globbyFiles.splice(
  189. 0,
  190. this.globbyFiles.length,
  191. 'c.tex',
  192. 'a.tex',
  193. 'a/a.tex',
  194. 'b.tex'
  195. )
  196. })
  197. it('processes the root folder files first, and then the subfolder, in alphabetical order', function(done) {
  198. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  199. '/foo',
  200. (error, path) => {
  201. expect(error).not.to.exist
  202. expect(path).not.to.exist
  203. sinon.assert.callOrder(
  204. this.fs.readFile.withArgs('/foo/a.tex'),
  205. this.fs.readFile.withArgs('/foo/b.tex'),
  206. this.fs.readFile.withArgs('/foo/c.tex'),
  207. this.fs.readFile.withArgs('/foo/a/a.tex')
  208. )
  209. return done()
  210. }
  211. )
  212. })
  213. it('processes smaller files first', function(done) {
  214. this.fs.stat.withArgs('/foo/c.tex').callsArgWith(1, null, { size: 1 })
  215. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  216. '/foo',
  217. (error, path) => {
  218. expect(error).not.to.exist
  219. expect(path).not.to.exist
  220. sinon.assert.callOrder(
  221. this.fs.readFile.withArgs('/foo/c.tex'),
  222. this.fs.readFile.withArgs('/foo/a.tex'),
  223. this.fs.readFile.withArgs('/foo/b.tex'),
  224. this.fs.readFile.withArgs('/foo/a/a.tex')
  225. )
  226. return done()
  227. }
  228. )
  229. })
  230. })
  231. describe('when main.tex contains a documentclass', function() {
  232. beforeEach(function() {
  233. return this.fs.readFile
  234. .withArgs('/foo/main.tex')
  235. .callsArgWith(2, null, this.documentclassContent)
  236. })
  237. it('returns main.tex', function(done) {
  238. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  239. '/foo',
  240. (error, path, content) => {
  241. expect(error).not.to.exist
  242. expect(path).to.equal('main.tex')
  243. expect(content).to.equal(this.documentclassContent)
  244. return done()
  245. }
  246. )
  247. })
  248. it('processes main.text first and stops processing when it finds the content', function(done) {
  249. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  250. '/foo',
  251. () => {
  252. expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
  253. expect(this.fs.readFile).not.to.be.calledWith('/foo/a.tex')
  254. return done()
  255. }
  256. )
  257. })
  258. })
  259. describe('when a.tex contains a documentclass', function() {
  260. beforeEach(function() {
  261. return this.fs.readFile
  262. .withArgs('/foo/a.tex')
  263. .callsArgWith(2, null, this.documentclassContent)
  264. })
  265. it('returns a.tex', function(done) {
  266. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  267. '/foo',
  268. (error, path, content) => {
  269. expect(error).not.to.exist
  270. expect(path).to.equal('a.tex')
  271. expect(content).to.equal(this.documentclassContent)
  272. return done()
  273. }
  274. )
  275. })
  276. it('processes main.text first and stops processing when it finds the content', function(done) {
  277. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  278. '/foo',
  279. () => {
  280. expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
  281. expect(this.fs.readFile).to.be.calledWith('/foo/a.tex')
  282. expect(this.fs.readFile).not.to.be.calledWith('/foo/b.tex')
  283. return done()
  284. }
  285. )
  286. })
  287. })
  288. describe('when there is no documentclass', function() {
  289. it('returns null with no error', function(done) {
  290. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  291. '/foo',
  292. (error, path, content) => {
  293. expect(error).not.to.exist
  294. expect(path).not.to.exist
  295. expect(content).not.to.exist
  296. return done()
  297. }
  298. )
  299. })
  300. it('processes all the files', function(done) {
  301. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  302. '/foo',
  303. () => {
  304. expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
  305. expect(this.fs.readFile).to.be.calledWith('/foo/a.tex')
  306. expect(this.fs.readFile).to.be.calledWith('/foo/b.tex')
  307. return done()
  308. }
  309. )
  310. })
  311. })
  312. describe('when there is an error reading a file', function() {
  313. beforeEach(function() {
  314. return this.fs.readFile
  315. .withArgs('/foo/a.tex')
  316. .callsArgWith(2, new Error('something went wrong'))
  317. })
  318. it('returns an error', function(done) {
  319. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  320. '/foo',
  321. (error, path, content) => {
  322. expect(error).to.exist
  323. expect(path).not.to.exist
  324. expect(content).not.to.exist
  325. return done()
  326. }
  327. )
  328. })
  329. })
  330. })
  331. describe('setRootDocFromName', function() {
  332. describe('when there is a suitable root doc', function() {
  333. beforeEach(function(done) {
  334. this.docPaths = {
  335. 'doc-id-1': '/chapter1.tex',
  336. 'doc-id-2': '/main.tex',
  337. 'doc-id-3': '/nested/chapter1a.tex',
  338. 'doc-id-4': '/nested/chapter1b.tex'
  339. }
  340. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  341. .stub()
  342. .callsArgWith(1, null, this.docPaths)
  343. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  344. .stub()
  345. .callsArgWith(2)
  346. return this.ProjectRootDocManager.setRootDocFromName(
  347. this.project_id,
  348. '/main.tex',
  349. done
  350. )
  351. })
  352. it('should check the docs of the project', function() {
  353. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  354. .calledWith(this.project_id)
  355. .should.equal(true)
  356. })
  357. it('should set the root doc to main.tex', function() {
  358. return this.ProjectEntityUpdateHandler.setRootDoc
  359. .calledWith(this.project_id, 'doc-id-2')
  360. .should.equal(true)
  361. })
  362. })
  363. describe('when there is a suitable root doc but the leading slash is missing', function() {
  364. beforeEach(function(done) {
  365. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  366. .stub()
  367. .callsArgWith(1, null, this.docPaths)
  368. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  369. .stub()
  370. .callsArgWith(2)
  371. return this.ProjectRootDocManager.setRootDocFromName(
  372. this.project_id,
  373. 'main.tex',
  374. done
  375. )
  376. })
  377. it('should check the docs of the project', function() {
  378. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  379. .calledWith(this.project_id)
  380. .should.equal(true)
  381. })
  382. it('should set the root doc to main.tex', function() {
  383. return this.ProjectEntityUpdateHandler.setRootDoc
  384. .calledWith(this.project_id, 'doc-id-2')
  385. .should.equal(true)
  386. })
  387. })
  388. describe('when there is a suitable root doc with a basename match', function() {
  389. beforeEach(function(done) {
  390. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  391. .stub()
  392. .callsArgWith(1, null, this.docPaths)
  393. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  394. .stub()
  395. .callsArgWith(2)
  396. return this.ProjectRootDocManager.setRootDocFromName(
  397. this.project_id,
  398. 'chapter1a.tex',
  399. done
  400. )
  401. })
  402. it('should check the docs of the project', function() {
  403. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  404. .calledWith(this.project_id)
  405. .should.equal(true)
  406. })
  407. it('should set the root doc using the basename', function() {
  408. return this.ProjectEntityUpdateHandler.setRootDoc
  409. .calledWith(this.project_id, 'doc-id-3')
  410. .should.equal(true)
  411. })
  412. })
  413. describe('when there is a suitable root doc but the filename is in quotes', function() {
  414. beforeEach(function(done) {
  415. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  416. .stub()
  417. .callsArgWith(1, null, this.docPaths)
  418. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  419. .stub()
  420. .callsArgWith(2)
  421. return this.ProjectRootDocManager.setRootDocFromName(
  422. this.project_id,
  423. "'main.tex'",
  424. done
  425. )
  426. })
  427. it('should check the docs of the project', function() {
  428. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  429. .calledWith(this.project_id)
  430. .should.equal(true)
  431. })
  432. it('should set the root doc to main.tex', function() {
  433. return this.ProjectEntityUpdateHandler.setRootDoc
  434. .calledWith(this.project_id, 'doc-id-2')
  435. .should.equal(true)
  436. })
  437. })
  438. describe('when there is no suitable root doc', function() {
  439. beforeEach(function(done) {
  440. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  441. .stub()
  442. .callsArgWith(1, null, this.docPaths)
  443. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  444. .stub()
  445. .callsArgWith(2)
  446. return this.ProjectRootDocManager.setRootDocFromName(
  447. this.project_id,
  448. 'other.tex',
  449. done
  450. )
  451. })
  452. it('should not set the root doc', function() {
  453. return this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(
  454. false
  455. )
  456. })
  457. })
  458. })
  459. describe('ensureRootDocumentIsSet', function() {
  460. beforeEach(function() {
  461. this.project = {}
  462. this.ProjectGetter.getProject = sinon
  463. .stub()
  464. .callsArgWith(2, null, this.project)
  465. return (this.ProjectRootDocManager.setRootDocAutomatically = sinon
  466. .stub()
  467. .callsArgWith(1, null))
  468. })
  469. describe('when the root doc is set', function() {
  470. beforeEach(function() {
  471. this.project.rootDoc_id = 'root-doc-id'
  472. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  473. this.project_id,
  474. this.callback
  475. )
  476. })
  477. it('should find the project fetching only the rootDoc_id field', function() {
  478. return this.ProjectGetter.getProject
  479. .calledWith(this.project_id, { rootDoc_id: 1 })
  480. .should.equal(true)
  481. })
  482. it('should not try to update the project rootDoc_id', function() {
  483. return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
  484. false
  485. )
  486. })
  487. it('should call the callback', function() {
  488. return this.callback.called.should.equal(true)
  489. })
  490. })
  491. describe('when the root doc is not set', function() {
  492. beforeEach(function() {
  493. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  494. this.project_id,
  495. this.callback
  496. )
  497. })
  498. it('should find the project with only the rootDoc_id field', function() {
  499. return this.ProjectGetter.getProject
  500. .calledWith(this.project_id, { rootDoc_id: 1 })
  501. .should.equal(true)
  502. })
  503. it('should update the project rootDoc_id', function() {
  504. return this.ProjectRootDocManager.setRootDocAutomatically
  505. .calledWith(this.project_id)
  506. .should.equal(true)
  507. })
  508. it('should call the callback', function() {
  509. return this.callback.called.should.equal(true)
  510. })
  511. })
  512. describe('when the project does not exist', function() {
  513. beforeEach(function() {
  514. this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
  515. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  516. this.project_id,
  517. this.callback
  518. )
  519. })
  520. it('should call the callback with an error', function() {
  521. return this.callback
  522. .calledWith(new Error('project not found'))
  523. .should.equal(true)
  524. })
  525. })
  526. })
  527. describe('ensureRootDocumentIsValid', function() {
  528. beforeEach(function() {
  529. this.project = {}
  530. this.ProjectGetter.getProject = sinon
  531. .stub()
  532. .callsArgWith(2, null, this.project)
  533. this.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().yields()
  534. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  535. .stub()
  536. .callsArgWith(1, null, this.docPaths)
  537. return (this.ProjectRootDocManager.setRootDocAutomatically = sinon
  538. .stub()
  539. .callsArgWith(1, null))
  540. })
  541. describe('when the root doc is set', function() {
  542. describe('when the root doc is valid', function() {
  543. beforeEach(function() {
  544. this.project.rootDoc_id = 'doc-id-2'
  545. return this.ProjectRootDocManager.ensureRootDocumentIsValid(
  546. this.project_id,
  547. this.callback
  548. )
  549. })
  550. it('should find the project fetching only the rootDoc_id field', function() {
  551. return this.ProjectGetter.getProject
  552. .calledWith(this.project_id, { rootDoc_id: 1 })
  553. .should.equal(true)
  554. })
  555. it('should not try to update the project rootDoc_id', function() {
  556. return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
  557. false
  558. )
  559. })
  560. it('should call the callback', function() {
  561. return this.callback.called.should.equal(true)
  562. })
  563. })
  564. describe('when the root doc is not valid', function() {
  565. beforeEach(function() {
  566. this.project.rootDoc_id = 'bogus-doc-id'
  567. return this.ProjectRootDocManager.ensureRootDocumentIsValid(
  568. this.project_id,
  569. this.callback
  570. )
  571. })
  572. it('should find the project fetching only the rootDoc_id field', function() {
  573. return this.ProjectGetter.getProject
  574. .calledWith(this.project_id, { rootDoc_id: 1 })
  575. .should.equal(true)
  576. })
  577. it('should null the rootDoc_id field', function() {
  578. return this.ProjectEntityUpdateHandler.setRootDoc
  579. .calledWith(this.project_id, null)
  580. .should.equal(true)
  581. })
  582. it('should try to find a new rootDoc', function() {
  583. return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
  584. true
  585. )
  586. })
  587. it('should call the callback', function() {
  588. return this.callback.called.should.equal(true)
  589. })
  590. })
  591. })
  592. describe('when the root doc is not set', function() {
  593. beforeEach(function() {
  594. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  595. this.project_id,
  596. this.callback
  597. )
  598. })
  599. it('should find the project fetching only the rootDoc_id fiel', function() {
  600. return this.ProjectGetter.getProject
  601. .calledWith(this.project_id, { rootDoc_id: 1 })
  602. .should.equal(true)
  603. })
  604. it('should update the project rootDoc_id', function() {
  605. return this.ProjectRootDocManager.setRootDocAutomatically
  606. .calledWith(this.project_id)
  607. .should.equal(true)
  608. })
  609. it('should call the callback', function() {
  610. return this.callback.called.should.equal(true)
  611. })
  612. })
  613. describe('when the project does not exist', function() {
  614. beforeEach(function() {
  615. this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
  616. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  617. this.project_id,
  618. this.callback
  619. )
  620. })
  621. it('should call the callback with an error', function() {
  622. return this.callback
  623. .calledWith(new Error('project not found'))
  624. .should.equal(true)
  625. })
  626. })
  627. })
  628. })