ProjectDuplicateNameTests.mjs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import Path from 'node:path'
  4. import fs from 'node:fs'
  5. import _ from 'lodash'
  6. import User from './helpers/User.mjs'
  7. import UserHelper from './helpers/UserHelper.mjs'
  8. import MockDocstoreApiClass from './mocks/MockDocstoreApi.mjs'
  9. import MockV1HistoryApiClass from './mocks/MockV1HistoryApi.mjs'
  10. import { fileURLToPath } from 'node:url'
  11. let MockDocstoreApi, MockV1HistoryApi
  12. const __dirname = fileURLToPath(new URL('.', import.meta.url))
  13. before(function () {
  14. MockDocstoreApi = MockDocstoreApiClass.instance()
  15. MockV1HistoryApi = MockV1HistoryApiClass.instance()
  16. })
  17. describe('ProjectDuplicateNames', function () {
  18. beforeEach(function (done) {
  19. this.owner = new User()
  20. this.owner.login(done)
  21. this.project = {}
  22. this.callback = sinon.stub()
  23. })
  24. describe('creating a project from the example template', function () {
  25. beforeEach(function (done) {
  26. this.owner.createProject(
  27. 'example-project',
  28. { template: 'example' },
  29. (error, projectId) => {
  30. expect(error).to.not.exist
  31. this.example_project_id = projectId
  32. this.owner.getProject(projectId, (error, project) => {
  33. expect(error).to.not.exist
  34. this.project = project
  35. this.mainTexDoc = _.find(
  36. project.rootFolder[0].docs,
  37. doc => doc.name === 'main.tex'
  38. )
  39. this.refBibDoc = _.find(
  40. project.rootFolder[0].docs,
  41. doc => doc.name === 'sample.bib'
  42. )
  43. this.imageFile = _.find(
  44. project.rootFolder[0].fileRefs,
  45. file => file.name === 'frog.jpg'
  46. )
  47. this.rootFolderId = project.rootFolder[0]._id.toString()
  48. // create a folder called 'testfolder'
  49. this.owner.request.post(
  50. {
  51. uri: `/project/${this.example_project_id}/folder`,
  52. json: {
  53. name: 'testfolder',
  54. parent_folder_id: this.rootFolderId,
  55. },
  56. },
  57. (err, res, body) => {
  58. expect(err).to.not.exist
  59. this.testFolderId = body._id
  60. done()
  61. }
  62. )
  63. })
  64. }
  65. )
  66. })
  67. it('should create a project', function () {
  68. expect(this.project.rootFolder[0].docs.length).to.equal(2)
  69. expect(this.project.rootFolder[0].fileRefs.length).to.equal(1)
  70. })
  71. it('should create two docs in the docstore', function () {
  72. const docs = MockDocstoreApi.docs[this.example_project_id]
  73. expect(Object.keys(docs).length).to.equal(2)
  74. })
  75. it('should create one file in the history-v1', function () {
  76. const files =
  77. MockV1HistoryApi.blobs[this.project.overleaf.history.id.toString()]
  78. expect(Object.keys(files).length).to.equal(1)
  79. })
  80. describe('for an existing doc', function () {
  81. describe('trying to add a doc with the same name', function () {
  82. beforeEach(function (done) {
  83. this.owner.request.post(
  84. {
  85. uri: `/project/${this.example_project_id}/doc`,
  86. json: {
  87. name: 'main.tex',
  88. parent_folder_id: this.rootFolderId,
  89. },
  90. },
  91. (err, res, body) => {
  92. expect(err).to.not.exist
  93. this.res = res
  94. done()
  95. }
  96. )
  97. })
  98. it('should respond with 400 error status', function () {
  99. expect(this.res.statusCode).to.equal(400)
  100. })
  101. })
  102. describe('trying to add a folder with the same name', function () {
  103. beforeEach(function (done) {
  104. this.owner.request.post(
  105. {
  106. uri: `/project/${this.example_project_id}/folder`,
  107. json: {
  108. name: 'main.tex',
  109. parent_folder_id: this.rootFolderId,
  110. },
  111. },
  112. (err, res, body) => {
  113. expect(err).to.not.exist
  114. this.res = res
  115. done()
  116. }
  117. )
  118. })
  119. it('should respond with 400 error status', function () {
  120. expect(this.res.statusCode).to.equal(400)
  121. })
  122. })
  123. })
  124. describe('for an existing file', function () {
  125. describe('trying to add a doc with the same name', function () {
  126. beforeEach(function (done) {
  127. this.owner.request.post(
  128. {
  129. uri: `/project/${this.example_project_id}/doc`,
  130. json: {
  131. name: 'frog.jpg',
  132. parent_folder_id: this.rootFolderId,
  133. },
  134. },
  135. (err, res, body) => {
  136. expect(err).to.not.exist
  137. this.res = res
  138. done()
  139. }
  140. )
  141. })
  142. it('should respond with 400 error status', function () {
  143. expect(this.res.statusCode).to.equal(400)
  144. })
  145. })
  146. describe('trying to add a folder with the same name', function () {
  147. beforeEach(function (done) {
  148. this.owner.request.post(
  149. {
  150. uri: `/project/${this.example_project_id}/folder`,
  151. json: {
  152. name: 'frog.jpg',
  153. parent_folder_id: this.rootFolderId,
  154. },
  155. },
  156. (err, res, body) => {
  157. expect(err).to.not.exist
  158. this.res = res
  159. done()
  160. }
  161. )
  162. })
  163. it('should respond with 400 error status', function () {
  164. expect(this.res.statusCode).to.equal(400)
  165. })
  166. })
  167. describe('trying to upload a file with the same name', function () {
  168. beforeEach(function (done) {
  169. this.owner.request.post(
  170. {
  171. uri: `/project/${this.example_project_id}/upload`,
  172. json: true,
  173. qs: {
  174. folder_id: this.rootFolderId,
  175. qqfilename: 'frog.jpg',
  176. },
  177. formData: {
  178. name: 'frog.jpg',
  179. qqfile: {
  180. value: fs.createReadStream(
  181. Path.join(__dirname, '/../files/1pixel.png')
  182. ),
  183. options: {
  184. filename: 'frog.jpg',
  185. contentType: 'image/jpeg',
  186. },
  187. },
  188. },
  189. },
  190. (err, res, body) => {
  191. expect(err).to.not.exist
  192. this.body = body
  193. // update the image id because we have replaced the file
  194. this.imageFile._id = this.body.entity_id
  195. done()
  196. }
  197. )
  198. })
  199. it('should succeed (overwriting the file)', function () {
  200. expect(this.body.success).to.equal(true)
  201. })
  202. })
  203. })
  204. // at this point the @imageFile._id has changed
  205. describe('for an existing folder', function () {
  206. describe('trying to add a doc with the same name', function () {
  207. beforeEach(function (done) {
  208. this.owner.request.post(
  209. {
  210. uri: `/project/${this.example_project_id}/doc`,
  211. json: {
  212. name: 'testfolder',
  213. parent_folder_id: this.rootFolderId,
  214. },
  215. },
  216. (err, res, body) => {
  217. expect(err).to.not.exist
  218. this.res = res
  219. done()
  220. }
  221. )
  222. })
  223. it('should respond with 400 error status', function () {
  224. expect(this.res.statusCode).to.equal(400)
  225. })
  226. })
  227. describe('trying to add a folder with the same name', function () {
  228. beforeEach(function (done) {
  229. this.owner.request.post(
  230. {
  231. uri: `/project/${this.example_project_id}/folder`,
  232. json: {
  233. name: 'testfolder',
  234. parent_folder_id: this.rootFolderId,
  235. },
  236. },
  237. (err, res, body) => {
  238. expect(err).to.not.exist
  239. this.res = res
  240. done()
  241. }
  242. )
  243. })
  244. it('should respond with 400 error status', function () {
  245. expect(this.res.statusCode).to.equal(400)
  246. })
  247. })
  248. describe('trying to upload a file with the same name', function () {
  249. beforeEach(function (done) {
  250. this.owner.request.post(
  251. {
  252. uri: `/project/${this.example_project_id}/upload`,
  253. json: true,
  254. qs: {
  255. folder_id: this.rootFolderId,
  256. qqfilename: 'frog.jpg',
  257. },
  258. formData: {
  259. qqfile: {
  260. value: fs.createReadStream(
  261. Path.join(__dirname, '/../files/1pixel.png')
  262. ),
  263. options: {
  264. filename: 'testfolder',
  265. contentType: 'image/jpeg',
  266. },
  267. },
  268. },
  269. },
  270. (err, res, body) => {
  271. expect(err).to.not.exist
  272. this.body = body
  273. done()
  274. }
  275. )
  276. })
  277. it('should respond with failure status', function () {
  278. expect(this.body.success).to.equal(false)
  279. })
  280. })
  281. })
  282. describe('rename for an existing doc', function () {
  283. describe('trying to rename a doc to the same name', function () {
  284. beforeEach(function (done) {
  285. this.owner.request.post(
  286. {
  287. uri: `/project/${this.example_project_id}/doc/${this.refBibDoc._id}/rename`,
  288. json: {
  289. name: 'main.tex',
  290. },
  291. },
  292. (err, res, body) => {
  293. expect(err).to.not.exist
  294. this.res = res
  295. done()
  296. }
  297. )
  298. })
  299. it('should respond with 400 error status', function () {
  300. expect(this.res.statusCode).to.equal(400)
  301. })
  302. })
  303. describe('trying to rename a folder to the same name', function () {
  304. beforeEach(function (done) {
  305. this.owner.request.post(
  306. {
  307. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/rename`,
  308. json: {
  309. name: 'main.tex',
  310. },
  311. },
  312. (err, res, body) => {
  313. expect(err).to.not.exist
  314. this.res = res
  315. done()
  316. }
  317. )
  318. })
  319. it('should respond with 400 error status', function () {
  320. expect(this.res.statusCode).to.equal(400)
  321. })
  322. })
  323. describe('trying to rename a file to the same name', function () {
  324. beforeEach(function (done) {
  325. this.owner.request.post(
  326. {
  327. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/rename`,
  328. json: {
  329. name: 'main.tex',
  330. },
  331. },
  332. (err, res, body) => {
  333. expect(err).to.not.exist
  334. this.res = res
  335. done()
  336. }
  337. )
  338. })
  339. it('should respond with failure status', function () {
  340. expect(this.res.statusCode).to.equal(400)
  341. })
  342. })
  343. })
  344. describe('rename for an existing file', function () {
  345. describe('trying to rename a doc to the same name', function () {
  346. beforeEach(function (done) {
  347. this.owner.request.post(
  348. {
  349. uri: `/project/${this.example_project_id}/doc/${this.refBibDoc._id}/rename`,
  350. json: {
  351. name: 'frog.jpg',
  352. },
  353. },
  354. (err, res, body) => {
  355. expect(err).to.not.exist
  356. this.res = res
  357. done()
  358. }
  359. )
  360. })
  361. it('should respond with 400 error status', function () {
  362. expect(this.res.statusCode).to.equal(400)
  363. })
  364. })
  365. describe('trying to rename a folder to the same name', function () {
  366. beforeEach(function (done) {
  367. this.owner.request.post(
  368. {
  369. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/rename`,
  370. json: {
  371. name: 'frog.jpg',
  372. },
  373. },
  374. (err, res, body) => {
  375. expect(err).to.not.exist
  376. this.res = res
  377. done()
  378. }
  379. )
  380. })
  381. it('should respond with 400 error status', function () {
  382. expect(this.res.statusCode).to.equal(400)
  383. })
  384. })
  385. describe('trying to rename a file to the same name', function () {
  386. beforeEach(function (done) {
  387. this.owner.request.post(
  388. {
  389. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/rename`,
  390. json: {
  391. name: 'frog.jpg',
  392. },
  393. },
  394. (err, res, body) => {
  395. expect(err).to.not.exist
  396. this.res = res
  397. done()
  398. }
  399. )
  400. })
  401. it('should respond with failure status', function () {
  402. expect(this.res.statusCode).to.equal(400)
  403. })
  404. })
  405. })
  406. describe('rename for an existing folder', function () {
  407. describe('trying to rename a doc to the same name', function () {
  408. beforeEach(function (done) {
  409. this.owner.request.post(
  410. {
  411. uri: `/project/${this.example_project_id}/doc/${this.refBibDoc._id}/rename`,
  412. json: {
  413. name: 'testfolder',
  414. },
  415. },
  416. (err, res, body) => {
  417. expect(err).to.not.exist
  418. this.res = res
  419. done()
  420. }
  421. )
  422. })
  423. it('should respond with 400 error status', function () {
  424. expect(this.res.statusCode).to.equal(400)
  425. })
  426. })
  427. describe('trying to rename a folder to the same name', function () {
  428. beforeEach(function (done) {
  429. this.owner.request.post(
  430. {
  431. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/rename`,
  432. json: {
  433. name: 'testfolder',
  434. },
  435. },
  436. (err, res, body) => {
  437. expect(err).to.not.exist
  438. this.res = res
  439. done()
  440. }
  441. )
  442. })
  443. it('should respond with 400 error status', function () {
  444. expect(this.res.statusCode).to.equal(400)
  445. })
  446. })
  447. describe('trying to rename a file to the same name', function () {
  448. beforeEach(function (done) {
  449. this.owner.request.post(
  450. {
  451. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/rename`,
  452. json: {
  453. name: 'testfolder',
  454. },
  455. },
  456. (err, res, body) => {
  457. expect(err).to.not.exist
  458. this.res = res
  459. done()
  460. }
  461. )
  462. })
  463. it('should respond with failure status', function () {
  464. expect(this.res.statusCode).to.equal(400)
  465. })
  466. })
  467. })
  468. describe('for an existing folder with a file with the same name', function () {
  469. beforeEach(function (done) {
  470. this.owner.request.post(
  471. {
  472. uri: `/project/${this.example_project_id}/doc`,
  473. json: {
  474. name: 'main.tex',
  475. parent_folder_id: this.testFolderId,
  476. },
  477. },
  478. (err, res, body) => {
  479. if (err) {
  480. throw err
  481. }
  482. this.owner.request.post(
  483. {
  484. uri: `/project/${this.example_project_id}/doc`,
  485. json: {
  486. name: 'frog.jpg',
  487. parent_folder_id: this.testFolderId,
  488. },
  489. },
  490. (err, res, body) => {
  491. if (err) {
  492. throw err
  493. }
  494. this.owner.request.post(
  495. {
  496. uri: `/project/${this.example_project_id}/folder`,
  497. json: {
  498. name: 'otherFolder',
  499. parent_folder_id: this.testFolderId,
  500. },
  501. },
  502. (err, res, body) => {
  503. if (err) {
  504. throw err
  505. }
  506. this.subFolderId = body._id
  507. this.owner.request.post(
  508. {
  509. uri: `/project/${this.example_project_id}/folder`,
  510. json: {
  511. name: 'otherFolder',
  512. parent_folder_id: this.rootFolderId,
  513. },
  514. },
  515. (err, res, body) => {
  516. if (err) {
  517. throw err
  518. }
  519. this.otherFolderId = body._id
  520. done()
  521. }
  522. )
  523. }
  524. )
  525. }
  526. )
  527. }
  528. )
  529. })
  530. describe('trying to move a doc into the folder', function () {
  531. beforeEach(function (done) {
  532. this.owner.request.post(
  533. {
  534. uri: `/project/${this.example_project_id}/doc/${this.mainTexDoc._id}/move`,
  535. json: {
  536. folder_id: this.testFolderId,
  537. },
  538. },
  539. (err, res, body) => {
  540. expect(err).to.not.exist
  541. this.res = res
  542. done()
  543. }
  544. )
  545. })
  546. it('should respond with 400 error status', function () {
  547. expect(this.res.statusCode).to.equal(400)
  548. })
  549. })
  550. describe('trying to move a file into the folder', function () {
  551. beforeEach(function (done) {
  552. this.owner.request.post(
  553. {
  554. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/move`,
  555. json: {
  556. folder_id: this.testFolderId,
  557. },
  558. },
  559. (err, res, body) => {
  560. expect(err).to.not.exist
  561. this.res = res
  562. done()
  563. }
  564. )
  565. })
  566. it('should respond with 400 error status', function () {
  567. expect(this.res.statusCode).to.equal(400)
  568. })
  569. })
  570. describe('trying to move a folder into the folder', function () {
  571. beforeEach(function (done) {
  572. this.owner.request.post(
  573. {
  574. uri: `/project/${this.example_project_id}/folder/${this.otherFolderId}/move`,
  575. json: {
  576. folder_id: this.testFolderId,
  577. },
  578. },
  579. (err, res, body) => {
  580. expect(err).to.not.exist
  581. this.res = res
  582. done()
  583. }
  584. )
  585. })
  586. it('should respond with 400 error status', function () {
  587. expect(this.res.statusCode).to.equal(400)
  588. })
  589. })
  590. describe('trying to move a folder into a subfolder of itself', function () {
  591. beforeEach(function (done) {
  592. this.owner.request.post(
  593. {
  594. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/move`,
  595. json: {
  596. folder_id: this.subFolderId,
  597. },
  598. },
  599. (err, res, body) => {
  600. expect(err).to.not.exist
  601. this.res = res
  602. done()
  603. }
  604. )
  605. })
  606. it('should respond with 400 error status', function () {
  607. expect(this.res.statusCode).to.equal(400)
  608. })
  609. })
  610. })
  611. })
  612. describe('regex characters in title', function () {
  613. let response, userHelper
  614. beforeEach(async function () {
  615. userHelper = new UserHelper()
  616. userHelper = await UserHelper.createUser()
  617. userHelper = await UserHelper.loginUser(
  618. userHelper.getDefaultEmailPassword()
  619. )
  620. })
  621. it('should handle characters that would cause an invalid regular expression', async function () {
  622. const projectName = 'Example (test'
  623. response = await userHelper.fetch('/project/new', {
  624. method: 'POST',
  625. body: new URLSearchParams([['projectName', projectName]]),
  626. })
  627. const body = await response.json()
  628. expect(response.status).to.equal(200) // can create project
  629. response = await userHelper.fetch(`/project/${body.project_id}`)
  630. expect(response.status).to.equal(200) // can open project
  631. })
  632. })
  633. })