ProjectStructureTests.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. const { expect } = require('chai')
  2. const { ObjectId } = require('mongodb-legacy')
  3. const Path = require('path')
  4. const fs = require('fs')
  5. const Settings = require('@overleaf/settings')
  6. const _ = require('lodash')
  7. const ProjectGetter = require('../../../../../app/src/Features/Project/ProjectGetter')
  8. const User = require('../../../../../test/acceptance/src/helpers/User')
  9. const MockDocUpdaterApiClass = require('../../../../../test/acceptance/src/mocks/MockDocUpdaterApi')
  10. const FILES_PATH = Path.join(__dirname, '../../../../../test/acceptance/files')
  11. let MockDocUpdaterApi
  12. before(function () {
  13. MockDocUpdaterApi = MockDocUpdaterApiClass.instance()
  14. })
  15. describe('ProjectStructureChanges', function () {
  16. let owner
  17. beforeEach(function (done) {
  18. owner = new User()
  19. owner.login(done)
  20. })
  21. function createExampleProject(owner, callback) {
  22. owner.createProject(
  23. 'example-project',
  24. { template: 'example' },
  25. (error, projectId) => {
  26. if (error) {
  27. return callback(error)
  28. }
  29. ProjectGetter.getProject(projectId, (error, project) => {
  30. if (error) {
  31. return callback(error)
  32. }
  33. const rootFolderId = project.rootFolder[0]._id.toString()
  34. callback(null, projectId, rootFolderId)
  35. })
  36. }
  37. )
  38. }
  39. function createExampleDoc(owner, projectId, callback) {
  40. ProjectGetter.getProject(projectId, (error, project) => {
  41. if (error) {
  42. return callback(error)
  43. }
  44. owner.request.post(
  45. {
  46. uri: `project/${projectId}/doc`,
  47. json: {
  48. name: 'new.tex',
  49. parent_folder_id: project.rootFolder[0]._id,
  50. },
  51. },
  52. (error, res, body) => {
  53. if (error) {
  54. return callback(error)
  55. }
  56. if (res.statusCode < 200 || res.statusCode >= 300) {
  57. return callback(new Error(`failed to add doc ${res.statusCode}`))
  58. }
  59. callback(null, body._id)
  60. }
  61. )
  62. })
  63. }
  64. function createExampleFolder(owner, projectId, callback) {
  65. owner.request.post(
  66. {
  67. uri: `project/${projectId}/folder`,
  68. json: {
  69. name: 'foo',
  70. },
  71. },
  72. (error, res, body) => {
  73. if (error) {
  74. return callback(error)
  75. }
  76. if (res.statusCode < 200 || res.statusCode >= 300) {
  77. return callback(new Error(`failed to add doc ${res.statusCode}`))
  78. }
  79. callback(null, body._id)
  80. }
  81. )
  82. }
  83. function uploadFile(
  84. owner,
  85. projectId,
  86. folderId,
  87. file,
  88. name,
  89. contentType,
  90. callback
  91. ) {
  92. owner.uploadFileInProject(
  93. projectId,
  94. folderId,
  95. file,
  96. name,
  97. contentType,
  98. callback
  99. )
  100. }
  101. function uploadExampleFile(owner, projectId, folderId, callback) {
  102. owner.uploadExampleFileInProject(
  103. projectId,
  104. folderId,
  105. '1pixel.png',
  106. callback
  107. )
  108. }
  109. function uploadExampleProject(owner, zipFilename, options, callback) {
  110. if (typeof options === 'function') {
  111. callback = options
  112. options = {}
  113. }
  114. const zipFile = fs.createReadStream(Path.join(FILES_PATH, zipFilename))
  115. owner.request.post(
  116. {
  117. uri: 'project/new/upload',
  118. formData: {
  119. name: zipFilename,
  120. qqfile: zipFile,
  121. },
  122. },
  123. (error, res, body) => {
  124. if (error) {
  125. return callback(error)
  126. }
  127. if (
  128. !options.allowBadStatus &&
  129. (res.statusCode < 200 || res.statusCode >= 300)
  130. ) {
  131. return new Error(`failed to upload project ${res.statusCode}`)
  132. }
  133. callback(null, JSON.parse(body).project_id, res)
  134. }
  135. )
  136. }
  137. function moveItem(owner, projectId, type, itemId, folderId, callback) {
  138. owner.moveItemInProject(projectId, type, itemId, folderId, callback)
  139. }
  140. function renameItem(owner, projectId, type, itemId, name, callback) {
  141. owner.renameItemInProject(projectId, type, itemId, name, callback)
  142. }
  143. function deleteItem(owner, projectId, type, itemId, callback) {
  144. owner.deleteItemInProject(projectId, type, itemId, callback)
  145. }
  146. function verifyVersionIncremented(
  147. projectId,
  148. oldVersion,
  149. updateVersion,
  150. increment,
  151. callback
  152. ) {
  153. expect(updateVersion).to.equal(oldVersion + increment)
  154. ProjectGetter.getProject(projectId, (error, newProject) => {
  155. if (error) {
  156. return callback(error)
  157. }
  158. expect(newProject.version).to.equal(updateVersion)
  159. callback()
  160. })
  161. }
  162. describe('creating a project from the example template', function () {
  163. let exampleProjectId
  164. beforeEach(function (done) {
  165. createExampleProject(owner, (err, projectId) => {
  166. exampleProjectId = projectId
  167. done(err)
  168. })
  169. })
  170. it('should version creating a doc and a file', function () {
  171. const { updates, version } =
  172. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  173. expect(updates.length).to.equal(3)
  174. for (const update of updates.slice(0, 2)) {
  175. expect(update.type).to.equal('add-doc')
  176. expect(update.userId).to.equal(owner._id)
  177. expect(update.docLines).to.be.a('string')
  178. }
  179. expect(_.filter(updates, { pathname: '/main.tex' }).length).to.equal(1)
  180. expect(_.filter(updates, { pathname: '/sample.bib' }).length).to.equal(1)
  181. expect(updates[2].type).to.equal('add-file')
  182. expect(updates[2].userId).to.equal(owner._id)
  183. expect(updates[2].pathname).to.equal('/frog.jpg')
  184. expect(updates[2].url).to.be.a('string')
  185. expect(version).to.equal(3)
  186. })
  187. })
  188. describe('duplicating a project', function () {
  189. let dupProjectId
  190. beforeEach(function (done) {
  191. createExampleProject(owner, (err, projectId) => {
  192. if (err) {
  193. return done(err)
  194. }
  195. owner.request.post(
  196. {
  197. uri: `/Project/${projectId}/clone`,
  198. json: {
  199. projectName: 'new.tex',
  200. },
  201. },
  202. (error, res, body) => {
  203. if (error) {
  204. throw error
  205. }
  206. if (res.statusCode < 200 || res.statusCode >= 300) {
  207. throw new Error(`failed to clone project ${res.statusCode}`)
  208. }
  209. dupProjectId = body.project_id
  210. done()
  211. }
  212. )
  213. })
  214. })
  215. it('should version the docs and files created', function () {
  216. const { updates, version } =
  217. MockDocUpdaterApi.getProjectStructureUpdates(dupProjectId)
  218. expect(updates.length).to.equal(3)
  219. for (const update of updates.slice(0, 2)) {
  220. expect(update.type).to.equal('add-doc')
  221. expect(update.userId).to.equal(owner._id)
  222. expect(update.docLines).to.be.a('string')
  223. }
  224. expect(_.filter(updates, { pathname: '/main.tex' }).length).to.equal(1)
  225. expect(_.filter(updates, { pathname: '/sample.bib' }).length).to.equal(1)
  226. expect(updates[2].type).to.equal('add-file')
  227. expect(updates[2].userId).to.equal(owner._id)
  228. expect(updates[2].pathname).to.equal('/frog.jpg')
  229. expect(updates[2].url).to.be.a('string')
  230. expect(version).to.equal(1)
  231. })
  232. })
  233. describe('adding a doc', function () {
  234. let exampleProjectId, oldVersion
  235. beforeEach(function (done) {
  236. createExampleProject(owner, (err, projectId) => {
  237. if (err) {
  238. return done(err)
  239. }
  240. exampleProjectId = projectId
  241. MockDocUpdaterApi.reset()
  242. ProjectGetter.getProject(projectId, (error, project) => {
  243. if (error) {
  244. return done(error)
  245. }
  246. oldVersion = project.version
  247. createExampleDoc(owner, projectId, done)
  248. })
  249. })
  250. })
  251. it('should version the doc added', function (done) {
  252. const { updates, version: newVersion } =
  253. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  254. expect(updates.length).to.equal(1)
  255. const update = updates[0]
  256. expect(update.type).to.equal('add-doc')
  257. expect(update.userId).to.equal(owner._id)
  258. expect(update.pathname).to.equal('/new.tex')
  259. expect(update.docLines).to.be.a('string')
  260. verifyVersionIncremented(
  261. exampleProjectId,
  262. oldVersion,
  263. newVersion,
  264. 1,
  265. done
  266. )
  267. })
  268. })
  269. describe('uploading a project', function () {
  270. let exampleProjectId
  271. beforeEach(function (done) {
  272. uploadExampleProject(owner, 'test_project.zip', (err, projectId) => {
  273. if (err) {
  274. return done(err)
  275. }
  276. exampleProjectId = projectId
  277. done()
  278. })
  279. })
  280. it('should version the docs and files created', function () {
  281. const { updates, version } =
  282. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  283. expect(updates.length).to.equal(2)
  284. expect(updates[0].type).to.equal('add-doc')
  285. expect(updates[0].userId).to.equal(owner._id)
  286. expect(updates[0].pathname).to.equal('/main.tex')
  287. expect(updates[0].docLines).to.equal('Test')
  288. expect(updates[1].type).to.equal('add-file')
  289. expect(updates[1].userId).to.equal(owner._id)
  290. expect(updates[1].pathname).to.equal('/1pixel.png')
  291. expect(updates[1].url).to.be.a('string')
  292. expect(version).to.equal(1)
  293. })
  294. })
  295. describe('uploading a project with files in different encodings', function () {
  296. let updates
  297. beforeEach(function (done) {
  298. uploadExampleProject(owner, 'charsets/charsets.zip', (err, projectId) => {
  299. if (err) {
  300. return done(err)
  301. }
  302. updates =
  303. MockDocUpdaterApi.getProjectStructureUpdates(projectId).updates
  304. done()
  305. })
  306. })
  307. it('should correctly parse windows-1252', function () {
  308. const update = _.find(
  309. updates,
  310. update => update.pathname === '/test-german-windows-1252.tex'
  311. )
  312. expect(update.docLines).to.contain(
  313. 'Der schnelle braune Fuchs sprang träge über den Hund.'
  314. )
  315. })
  316. it('should correctly parse German utf8', function () {
  317. const update = _.find(
  318. updates,
  319. update => update.pathname === '/test-german-utf8x.tex'
  320. )
  321. expect(update.docLines).to.contain(
  322. 'Der schnelle braune Fuchs sprang träge über den Hund.'
  323. )
  324. })
  325. it('should correctly parse little-endian utf16', function () {
  326. const update = _.find(
  327. updates,
  328. update => update.pathname === '/test-greek-utf16-le-bom.tex'
  329. )
  330. expect(update.docLines).to.contain(
  331. 'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
  332. )
  333. })
  334. it('should correctly parse Greek utf8', function () {
  335. const update = _.find(
  336. updates,
  337. update => update.pathname === '/test-greek-utf8x.tex'
  338. )
  339. expect(update.docLines).to.contain(
  340. 'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
  341. )
  342. })
  343. })
  344. describe('uploading a file', function () {
  345. let exampleProjectId, oldVersion, rootFolderId
  346. beforeEach(function (done) {
  347. createExampleProject(owner, (err, projectId, folderId) => {
  348. if (err) {
  349. return done(err)
  350. }
  351. exampleProjectId = projectId
  352. rootFolderId = folderId
  353. MockDocUpdaterApi.reset()
  354. ProjectGetter.getProject(projectId, (error, project) => {
  355. if (error) {
  356. throw error
  357. }
  358. oldVersion = project.version
  359. uploadExampleFile(owner, projectId, rootFolderId, done)
  360. })
  361. })
  362. })
  363. it('should version a newly uploaded file', function (done) {
  364. const { updates, version } =
  365. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  366. expect(updates.length).to.equal(1)
  367. const update = updates[0]
  368. expect(update.type).to.equal('add-file')
  369. expect(update.userId).to.equal(owner._id)
  370. expect(update.pathname).to.equal('/1pixel.png')
  371. expect(update.url).to.be.a('string')
  372. // one file upload
  373. verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
  374. })
  375. it('should version a replacement file', function (done) {
  376. MockDocUpdaterApi.reset()
  377. uploadFile(
  378. owner,
  379. exampleProjectId,
  380. rootFolderId,
  381. '2pixel.png',
  382. '1pixel.png',
  383. 'image/png',
  384. () => {
  385. const { updates, version } =
  386. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  387. expect(updates.length).to.equal(2)
  388. expect(updates[0].type).to.equal('rename-file')
  389. expect(updates[0].userId).to.equal(owner._id)
  390. expect(updates[0].pathname).to.equal('/1pixel.png')
  391. expect(updates[0].newPathname).to.equal('')
  392. expect(updates[1].type).to.equal('add-file')
  393. expect(updates[1].userId).to.equal(owner._id)
  394. expect(updates[1].pathname).to.equal('/1pixel.png')
  395. expect(updates[1].url).to.be.a('string')
  396. // two file uploads
  397. verifyVersionIncremented(
  398. exampleProjectId,
  399. oldVersion,
  400. version,
  401. 2,
  402. done
  403. )
  404. }
  405. )
  406. })
  407. })
  408. describe('moving entities', function () {
  409. let exampleProjectId,
  410. oldVersion,
  411. exampleDocId,
  412. exampleFileId,
  413. exampleFolderId
  414. beforeEach(function (done) {
  415. createExampleProject(owner, (err, projectId, rootFolderId) => {
  416. if (err) {
  417. return done(err)
  418. }
  419. exampleProjectId = projectId
  420. createExampleDoc(owner, projectId, (err, docId) => {
  421. if (err) {
  422. return done(err)
  423. }
  424. exampleDocId = docId
  425. uploadExampleFile(owner, projectId, rootFolderId, (err, fileId) => {
  426. if (err) {
  427. return done(err)
  428. }
  429. exampleFileId = fileId
  430. createExampleFolder(owner, projectId, (err, folderId) => {
  431. if (err) {
  432. return done(err)
  433. }
  434. exampleFolderId = folderId
  435. ProjectGetter.getProject(projectId, (error, project) => {
  436. if (error) {
  437. throw error
  438. }
  439. oldVersion = project.version
  440. MockDocUpdaterApi.reset()
  441. done()
  442. })
  443. })
  444. })
  445. })
  446. })
  447. })
  448. it('should version moving a doc', function (done) {
  449. moveItem(
  450. owner,
  451. exampleProjectId,
  452. 'doc',
  453. exampleDocId,
  454. exampleFolderId,
  455. () => {
  456. const { updates, version } =
  457. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  458. expect(updates.length).to.equal(1)
  459. const update = updates[0]
  460. expect(update.type).to.equal('rename-doc')
  461. expect(update.userId).to.equal(owner._id)
  462. expect(update.pathname).to.equal('/new.tex')
  463. expect(update.newPathname).to.equal('/foo/new.tex')
  464. // 2, because it's a delete and then add
  465. verifyVersionIncremented(
  466. exampleProjectId,
  467. oldVersion,
  468. version,
  469. 2,
  470. done
  471. )
  472. }
  473. )
  474. })
  475. it('should version moving a file', function (done) {
  476. moveItem(
  477. owner,
  478. exampleProjectId,
  479. 'file',
  480. exampleFileId,
  481. exampleFolderId,
  482. () => {
  483. const { updates, version } =
  484. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  485. expect(updates.length).to.equal(1)
  486. const update = updates[0]
  487. expect(update.type).to.equal('rename-file')
  488. expect(update.userId).to.equal(owner._id)
  489. expect(update.pathname).to.equal('/1pixel.png')
  490. expect(update.newPathname).to.equal('/foo/1pixel.png')
  491. // 2, because it's a delete and then add
  492. verifyVersionIncremented(
  493. exampleProjectId,
  494. oldVersion,
  495. version,
  496. 2,
  497. done
  498. )
  499. }
  500. )
  501. })
  502. it('should version moving a folder', function (done) {
  503. moveItem(
  504. owner,
  505. exampleProjectId,
  506. 'doc',
  507. exampleDocId,
  508. exampleFolderId,
  509. () => {
  510. MockDocUpdaterApi.reset()
  511. owner.request.post(
  512. {
  513. uri: `project/${exampleProjectId}/folder`,
  514. json: {
  515. name: 'bar',
  516. },
  517. },
  518. (error, res, body) => {
  519. if (error) {
  520. throw error
  521. }
  522. const newFolderId = body._id
  523. moveItem(
  524. owner,
  525. exampleProjectId,
  526. 'folder',
  527. exampleFolderId,
  528. newFolderId,
  529. () => {
  530. const { updates, version } =
  531. MockDocUpdaterApi.getProjectStructureUpdates(
  532. exampleProjectId
  533. )
  534. expect(updates.length).to.equal(1)
  535. const update = updates[0]
  536. expect(update.type).to.equal('rename-doc')
  537. expect(update.userId).to.equal(owner._id)
  538. expect(update.pathname).to.equal('/foo/new.tex')
  539. expect(update.newPathname).to.equal('/bar/foo/new.tex')
  540. // 5, because it's two file moves plus a folder
  541. verifyVersionIncremented(
  542. exampleProjectId,
  543. oldVersion,
  544. version,
  545. 5,
  546. done
  547. )
  548. }
  549. )
  550. }
  551. )
  552. }
  553. )
  554. })
  555. })
  556. describe('renaming entities', function () {
  557. let exampleProjectId,
  558. exampleDocId,
  559. exampleFileId,
  560. exampleFolderId,
  561. oldVersion
  562. beforeEach(function (done) {
  563. createExampleProject(owner, (err, projectId, rootFolderId) => {
  564. if (err) {
  565. return done(err)
  566. }
  567. exampleProjectId = projectId
  568. createExampleDoc(owner, projectId, (err, docId) => {
  569. if (err) {
  570. return done(err)
  571. }
  572. exampleDocId = docId
  573. uploadExampleFile(owner, projectId, rootFolderId, (err, fileId) => {
  574. if (err) {
  575. return done(err)
  576. }
  577. exampleFileId = fileId
  578. createExampleFolder(owner, projectId, (err, folderId) => {
  579. if (err) {
  580. return done(err)
  581. }
  582. exampleFolderId = folderId
  583. moveItem(owner, projectId, 'doc', docId, folderId, () => {
  584. moveItem(owner, projectId, 'file', fileId, folderId, () => {
  585. MockDocUpdaterApi.reset()
  586. ProjectGetter.getProject(
  587. exampleProjectId,
  588. (error, project) => {
  589. if (error) {
  590. throw error
  591. }
  592. oldVersion = project.version
  593. done()
  594. }
  595. )
  596. })
  597. })
  598. })
  599. })
  600. })
  601. })
  602. })
  603. it('should version renaming a doc', function (done) {
  604. renameItem(
  605. owner,
  606. exampleProjectId,
  607. 'Doc',
  608. exampleDocId,
  609. 'wombat.tex',
  610. () => {
  611. const { updates, version } =
  612. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  613. expect(updates.length).to.equal(1)
  614. const update = updates[0]
  615. expect(update.type).to.equal('rename-doc')
  616. expect(update.userId).to.equal(owner._id)
  617. expect(update.pathname).to.equal('/foo/new.tex')
  618. expect(update.newPathname).to.equal('/foo/wombat.tex')
  619. verifyVersionIncremented(
  620. exampleProjectId,
  621. oldVersion,
  622. version,
  623. 1,
  624. done
  625. )
  626. }
  627. )
  628. })
  629. it('should version renaming a file', function (done) {
  630. renameItem(
  631. owner,
  632. exampleProjectId,
  633. 'file',
  634. exampleFileId,
  635. 'potato.png',
  636. () => {
  637. const { updates, version } =
  638. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  639. expect(updates.length).to.equal(1)
  640. const update = updates[0]
  641. expect(update.type).to.equal('rename-file')
  642. expect(update.userId).to.equal(owner._id)
  643. expect(update.pathname).to.equal('/foo/1pixel.png')
  644. expect(update.newPathname).to.equal('/foo/potato.png')
  645. verifyVersionIncremented(
  646. exampleProjectId,
  647. oldVersion,
  648. version,
  649. 1,
  650. done
  651. )
  652. }
  653. )
  654. })
  655. it('should version renaming a folder', function (done) {
  656. renameItem(
  657. owner,
  658. exampleProjectId,
  659. 'folder',
  660. exampleFolderId,
  661. 'giraffe',
  662. () => {
  663. const { updates, version } =
  664. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  665. expect(updates.length).to.equal(2)
  666. expect(updates[0].type).to.equal('rename-doc')
  667. expect(updates[0].userId).to.equal(owner._id)
  668. expect(updates[0].pathname).to.equal('/foo/new.tex')
  669. expect(updates[0].newPathname).to.equal('/giraffe/new.tex')
  670. expect(updates[1].type).to.equal('rename-file')
  671. expect(updates[1].userId).to.equal(owner._id)
  672. expect(updates[1].pathname).to.equal('/foo/1pixel.png')
  673. expect(updates[1].newPathname).to.equal('/giraffe/1pixel.png')
  674. verifyVersionIncremented(
  675. exampleProjectId,
  676. oldVersion,
  677. version,
  678. 1,
  679. done
  680. )
  681. }
  682. )
  683. })
  684. })
  685. describe('deleting entities', function () {
  686. let exampleProjectId, oldVersion, exampleFolderId
  687. beforeEach(function (done) {
  688. createExampleProject(owner, (err, projectId) => {
  689. if (err) {
  690. return done(err)
  691. }
  692. exampleProjectId = projectId
  693. createExampleFolder(owner, exampleProjectId, (err, folderId) => {
  694. if (err) {
  695. return done(err)
  696. }
  697. exampleFolderId = folderId
  698. createExampleDoc(owner, projectId, (err, docId) => {
  699. if (err) {
  700. return done(err)
  701. }
  702. uploadExampleFile(owner, projectId, folderId, (err, fileId) => {
  703. if (err) {
  704. return done(err)
  705. }
  706. moveItem(owner, projectId, 'doc', docId, folderId, () => {
  707. moveItem(owner, projectId, 'file', fileId, folderId, () => {
  708. MockDocUpdaterApi.reset()
  709. ProjectGetter.getProject(
  710. exampleProjectId,
  711. (error, project) => {
  712. if (error) {
  713. throw error
  714. }
  715. oldVersion = project.version
  716. done()
  717. }
  718. )
  719. })
  720. })
  721. })
  722. })
  723. })
  724. })
  725. })
  726. it('should version deleting a folder', function (done) {
  727. deleteItem(owner, exampleProjectId, 'folder', exampleFolderId, () => {
  728. const { updates, version } =
  729. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  730. expect(updates.length).to.equal(2)
  731. expect(updates[0].type).to.equal('rename-doc')
  732. expect(updates[0].userId).to.equal(owner._id)
  733. expect(updates[0].pathname).to.equal('/foo/new.tex')
  734. expect(updates[0].newPathname).to.equal('')
  735. expect(updates[1].type).to.equal('rename-file')
  736. expect(updates[1].userId).to.equal(owner._id)
  737. expect(updates[1].pathname).to.equal('/foo/1pixel.png')
  738. expect(updates[1].newPathname).to.equal('')
  739. verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
  740. })
  741. })
  742. })
  743. describe('tpds', function () {
  744. let projectName, exampleProjectId, oldVersion, rootFolderId
  745. beforeEach(function (done) {
  746. projectName = `tpds-project-${new ObjectId().toString()}`
  747. owner.createProject(projectName, (error, projectId) => {
  748. if (error) {
  749. throw error
  750. }
  751. exampleProjectId = projectId
  752. fs.mkdir(Settings.path.dumpFolder, { recursive: true }, error => {
  753. if (error) {
  754. throw error
  755. }
  756. ProjectGetter.getProject(exampleProjectId, (error, project) => {
  757. if (error) {
  758. throw error
  759. }
  760. MockDocUpdaterApi.reset()
  761. rootFolderId = project.rootFolder[0]._id.toString()
  762. oldVersion = project.version
  763. done()
  764. })
  765. })
  766. })
  767. })
  768. it('should version adding a doc', function (done) {
  769. const req = owner.request.post({
  770. uri: `/user/${owner._id}/update/${projectName}/test.tex`,
  771. auth: {
  772. user: _.keys(Settings.httpAuthUsers)[0],
  773. pass: _.values(Settings.httpAuthUsers)[0],
  774. sendImmediately: true,
  775. },
  776. body: fs.createReadStream(Path.join(FILES_PATH, 'test.tex')),
  777. })
  778. req.on('error', err => {
  779. throw err
  780. })
  781. req.on('response', res => {
  782. if (res.statusCode < 200 || res.statusCode >= 300) {
  783. throw new Error(`failed to upload file ${res.statusCode}`)
  784. }
  785. const { updates, version } =
  786. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  787. expect(updates.length).to.equal(1)
  788. const update = updates[0]
  789. expect(update.type).to.equal('add-doc')
  790. expect(update.userId).to.equal(owner._id)
  791. expect(update.pathname).to.equal('/test.tex')
  792. expect(update.docLines).to.equal('Test')
  793. verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
  794. })
  795. })
  796. it('should version adding a new file', function (done) {
  797. const req = owner.request.post({
  798. uri: `/user/${owner._id}/update/${projectName}/1pixel.png`,
  799. auth: {
  800. user: _.keys(Settings.httpAuthUsers)[0],
  801. pass: _.values(Settings.httpAuthUsers)[0],
  802. sendImmediately: true,
  803. },
  804. body: fs.createReadStream(Path.join(FILES_PATH, '1pixel.png')),
  805. })
  806. req.on('error', err => {
  807. throw err
  808. })
  809. req.on('response', res => {
  810. if (res.statusCode < 200 || res.statusCode >= 300) {
  811. throw new Error(`failed to upload file ${res.statusCode}`)
  812. }
  813. const { updates, version } =
  814. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  815. expect(updates.length).to.equal(1)
  816. const update = updates[0]
  817. expect(update.type).to.equal('add-file')
  818. expect(update.userId).to.equal(owner._id)
  819. expect(update.pathname).to.equal('/1pixel.png')
  820. expect(update.url).to.be.a('string')
  821. verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
  822. })
  823. })
  824. describe('when there are files in the project', function () {
  825. beforeEach(function (done) {
  826. uploadExampleFile(owner, exampleProjectId, rootFolderId, () => {
  827. createExampleDoc(owner, exampleProjectId, () => {
  828. ProjectGetter.getProject(exampleProjectId, (error, project) => {
  829. if (error) {
  830. throw error
  831. }
  832. MockDocUpdaterApi.reset()
  833. oldVersion = project.version
  834. done()
  835. })
  836. })
  837. })
  838. })
  839. it('should version replacing a file', function (done) {
  840. const req = owner.request.post({
  841. uri: `/user/${owner._id}/update/${projectName}/1pixel.png`,
  842. auth: {
  843. user: _.keys(Settings.httpAuthUsers)[0],
  844. pass: _.values(Settings.httpAuthUsers)[0],
  845. sendImmediately: true,
  846. },
  847. body: fs.createReadStream(Path.join(FILES_PATH, '2pixel.png')),
  848. })
  849. req.on('error', err => {
  850. throw err
  851. })
  852. req.on('response', res => {
  853. if (res.statusCode < 200 || res.statusCode >= 300) {
  854. throw new Error(`failed to upload file ${res.statusCode}`)
  855. }
  856. const { updates, version } =
  857. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  858. expect(updates.length).to.equal(2)
  859. expect(updates[0].type).to.equal('rename-file')
  860. expect(updates[0].userId).to.equal(owner._id)
  861. expect(updates[0].pathname).to.equal('/1pixel.png')
  862. expect(updates[0].newPathname).to.equal('')
  863. expect(updates[1].type).to.equal('add-file')
  864. expect(updates[1].userId).to.equal(owner._id)
  865. expect(updates[1].pathname).to.equal('/1pixel.png')
  866. expect(updates[1].url).to.be.a('string')
  867. verifyVersionIncremented(
  868. exampleProjectId,
  869. oldVersion,
  870. version,
  871. 1,
  872. done
  873. )
  874. })
  875. })
  876. it('should version deleting a doc', function (done) {
  877. owner.request.delete(
  878. {
  879. uri: `/user/${owner._id}/update/${projectName}/new.tex`,
  880. auth: {
  881. user: _.keys(Settings.httpAuthUsers)[0],
  882. pass: _.values(Settings.httpAuthUsers)[0],
  883. sendImmediately: true,
  884. },
  885. },
  886. (error, res) => {
  887. if (error) {
  888. throw error
  889. }
  890. if (res.statusCode < 200 || res.statusCode >= 300) {
  891. throw new Error(`failed to delete doc ${res.statusCode}`)
  892. }
  893. const { updates, version } =
  894. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  895. expect(updates.length).to.equal(1)
  896. const update = updates[0]
  897. expect(update.type).to.equal('rename-doc')
  898. expect(update.userId).to.equal(owner._id)
  899. expect(update.pathname).to.equal('/new.tex')
  900. expect(update.newPathname).to.equal('')
  901. verifyVersionIncremented(
  902. exampleProjectId,
  903. oldVersion,
  904. version,
  905. 1,
  906. done
  907. )
  908. }
  909. )
  910. })
  911. })
  912. })
  913. describe('uploading a document', function () {
  914. let exampleProjectId, rootFolderId
  915. beforeEach(function (done) {
  916. createExampleProject(owner, (err, projectId, folderId) => {
  917. if (err) {
  918. return done(err)
  919. }
  920. exampleProjectId = projectId
  921. rootFolderId = folderId
  922. MockDocUpdaterApi.reset()
  923. done()
  924. })
  925. })
  926. describe('with an unusual character set', function () {
  927. it('should correctly handle utf16-le data', function (done) {
  928. uploadFile(
  929. owner,
  930. exampleProjectId,
  931. rootFolderId,
  932. 'charsets/test-greek-utf16-le-bom.tex',
  933. 'test-greek-utf16-le-bom.tex',
  934. 'text/x-tex',
  935. () => {
  936. const { updates } =
  937. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  938. expect(updates.length).to.equal(1)
  939. const update = updates[0]
  940. expect(update.type).to.equal('add-doc')
  941. expect(update.pathname).to.equal('/test-greek-utf16-le-bom.tex')
  942. expect(update.docLines).to.contain(
  943. 'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
  944. )
  945. done()
  946. }
  947. )
  948. })
  949. it('should correctly handle windows1252/iso-8859-1/latin1 data', function (done) {
  950. uploadFile(
  951. owner,
  952. exampleProjectId,
  953. rootFolderId,
  954. 'charsets/test-german-windows-1252.tex',
  955. 'test-german-windows-1252.tex',
  956. 'text/x-tex',
  957. () => {
  958. const { updates } =
  959. MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
  960. expect(updates.length).to.equal(1)
  961. const update = updates[0]
  962. expect(update.type).to.equal('add-doc')
  963. expect(update.pathname).to.equal('/test-german-windows-1252.tex')
  964. expect(update.docLines).to.contain(
  965. 'Der schnelle braune Fuchs sprang träge über den Hund.'
  966. )
  967. done()
  968. }
  969. )
  970. })
  971. })
  972. })
  973. })