overleaf/services/web/test/acceptance/src/ProjectStructureTests.js
Miguel Serrano f9871103bf Merge pull request #3949 from overleaf/msm-reenable-eslint-const-rule
Reenable eslint `prefer-const` rule

GitOrigin-RevId: 4f3825be8b8dff381095209085a36eaab76260d5
2021-05-06 02:09:14 +00:00

1404 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { expect } = require('chai')
const mkdirp = require('mkdirp')
const { ObjectId } = require('mongodb')
const Path = require('path')
const fs = require('fs')
const Settings = require('settings-sharelatex')
const _ = require('underscore')
const { Project } = require('../../../app/src/models/Project')
const ProjectGetter = require('../../../app/src/Features/Project/ProjectGetter.js')
const User = require('./helpers/User')
const MockDocStoreApiClass = require('./mocks/MockDocstoreApi')
const MockDocUpdaterApiClass = require('./mocks/MockDocUpdaterApi')
let MockDocStoreApi, MockDocUpdaterApi
before(function () {
MockDocUpdaterApi = MockDocUpdaterApiClass.instance()
MockDocStoreApi = MockDocStoreApiClass.instance()
})
describe('ProjectStructureChanges', function () {
let owner
beforeEach(function (done) {
owner = new User()
owner.login(done)
})
function createExampleProject(owner, callback) {
owner.createProject(
'example-project',
{ template: 'example' },
(error, projectId) => {
if (error) {
return callback(error)
}
ProjectGetter.getProject(projectId, (error, project) => {
if (error) {
return callback(error)
}
const rootFolderId = project.rootFolder[0]._id.toString()
callback(null, projectId, rootFolderId)
})
}
)
}
function createExampleDoc(owner, projectId, callback) {
ProjectGetter.getProject(projectId, (error, project) => {
if (error) {
return callback(error)
}
owner.request.post(
{
uri: `project/${projectId}/doc`,
json: {
name: 'new.tex',
parent_folder_id: project.rootFolder[0]._id,
},
},
(error, res, body) => {
if (error) {
return callback(error)
}
if (res.statusCode < 200 || res.statusCode >= 300) {
return callback(new Error(`failed to add doc ${res.statusCode}`))
}
callback(null, body._id)
}
)
})
}
function createExampleFolder(owner, projectId, callback) {
owner.request.post(
{
uri: `project/${projectId}/folder`,
json: {
name: 'foo',
},
},
(error, res, body) => {
if (error) {
return callback(error)
}
if (res.statusCode < 200 || res.statusCode >= 300) {
return callback(new Error(`failed to add doc ${res.statusCode}`))
}
callback(null, body._id)
}
)
}
function uploadFile(
owner,
projectId,
folderId,
file,
name,
contentType,
callback
) {
owner.uploadFileInProject(
projectId,
folderId,
file,
name,
contentType,
callback
)
}
function uploadExampleFile(owner, projectId, folderId, callback) {
owner.uploadExampleFileInProject(
projectId,
folderId,
'1pixel.png',
callback
)
}
function uploadExampleProject(owner, zipFilename, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
const zipFile = fs.createReadStream(
Path.resolve(Path.join(__dirname, '..', 'files', zipFilename))
)
owner.request.post(
{
uri: 'project/new/upload',
formData: {
qqfile: zipFile,
},
},
(error, res, body) => {
if (error) {
return callback(error)
}
if (
!options.allowBadStatus &&
(res.statusCode < 200 || res.statusCode >= 300)
) {
return new Error(`failed to upload project ${res.statusCode}`)
}
callback(null, JSON.parse(body).project_id, res)
}
)
}
function moveItem(owner, projectId, type, itemId, folderId, callback) {
owner.moveItemInProject(projectId, type, itemId, folderId, callback)
}
function renameItem(owner, projectId, type, itemId, name, callback) {
owner.renameItemInProject(projectId, type, itemId, name, callback)
}
function deleteItem(owner, projectId, type, itemId, callback) {
owner.deleteItemInProject(projectId, type, itemId, callback)
}
function verifyVersionIncremented(
projectId,
oldVersion,
updateVersion,
increment,
callback
) {
expect(updateVersion).to.equal(oldVersion + increment)
ProjectGetter.getProject(projectId, (error, newProject) => {
if (error) {
return callback(error)
}
expect(newProject.version).to.equal(updateVersion)
callback()
})
}
describe('creating a project from the example template', function () {
let exampleProjectId
beforeEach(function (done) {
createExampleProject(owner, (err, projectId) => {
exampleProjectId = projectId
done(err)
})
})
it('should version creating a doc and a file', function () {
const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
exampleProjectId
)
expect(updates.length).to.equal(3)
for (const update of updates.slice(0, 2)) {
expect(update.type).to.equal('add-doc')
expect(update.userId).to.equal(owner._id)
expect(update.docLines).to.be.a('string')
}
expect(_.where(updates, { pathname: '/main.tex' }).length).to.equal(1)
expect(_.where(updates, { pathname: '/references.bib' }).length).to.equal(
1
)
expect(updates[2].type).to.equal('add-file')
expect(updates[2].userId).to.equal(owner._id)
expect(updates[2].pathname).to.equal('/universe.jpg')
expect(updates[2].url).to.be.a('string')
expect(version).to.equal(3)
})
})
describe('duplicating a project', function () {
let dupProjectId
beforeEach(function (done) {
createExampleProject(owner, (err, projectId) => {
if (err) {
return done(err)
}
owner.request.post(
{
uri: `/Project/${projectId}/clone`,
json: {
projectName: 'new.tex',
},
},
(error, res, body) => {
if (error) {
throw error
}
if (res.statusCode < 200 || res.statusCode >= 300) {
throw new Error(`failed to clone project ${res.statusCode}`)
}
dupProjectId = body.project_id
done()
}
)
})
})
it('should version the docs and files created', function () {
const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
dupProjectId
)
expect(updates.length).to.equal(3)
for (const update of updates.slice(0, 2)) {
expect(update.type).to.equal('add-doc')
expect(update.userId).to.equal(owner._id)
expect(update.docLines).to.be.a('string')
}
expect(_.where(updates, { pathname: '/main.tex' }).length).to.equal(1)
expect(_.where(updates, { pathname: '/references.bib' }).length).to.equal(
1
)
expect(updates[2].type).to.equal('add-file')
expect(updates[2].userId).to.equal(owner._id)
expect(updates[2].pathname).to.equal('/universe.jpg')
expect(updates[2].url).to.be.a('string')
expect(version).to.equal(1)
})
})
describe('adding a doc', function () {
let exampleProjectId, oldVersion
beforeEach(function (done) {
createExampleProject(owner, (err, projectId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
MockDocUpdaterApi.reset()
ProjectGetter.getProject(projectId, (error, project) => {
if (error) {
return done(error)
}
oldVersion = project.version
createExampleDoc(owner, projectId, done)
})
})
})
it('should version the doc added', function (done) {
const {
updates,
version: newVersion,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('add-doc')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/new.tex')
expect(update.docLines).to.be.a('string')
verifyVersionIncremented(
exampleProjectId,
oldVersion,
newVersion,
1,
done
)
})
})
describe('uploading a project', function () {
let exampleProjectId
beforeEach(function (done) {
uploadExampleProject(owner, 'test_project.zip', (err, projectId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
done()
})
})
it('should version the docs and files created', function () {
const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
exampleProjectId
)
expect(updates.length).to.equal(2)
expect(updates[0].type).to.equal('add-doc')
expect(updates[0].userId).to.equal(owner._id)
expect(updates[0].pathname).to.equal('/main.tex')
expect(updates[0].docLines).to.equal('Test')
expect(updates[1].type).to.equal('add-file')
expect(updates[1].userId).to.equal(owner._id)
expect(updates[1].pathname).to.equal('/1pixel.png')
expect(updates[1].url).to.be.a('string')
expect(version).to.equal(1)
})
})
describe('uploading a project with a name', function () {
let exampleProjectId
const testProjectName = 'wombat'
beforeEach(function (done) {
uploadExampleProject(
owner,
'test_project_with_name.zip',
(err, projectId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
done()
}
)
})
it('should set the project name from the zip contents', function (done) {
ProjectGetter.getProject(exampleProjectId, (error, project) => {
expect(error).not.to.exist
expect(project.name).to.equal(testProjectName)
done()
})
})
})
describe('uploading a project with an invalid name', function () {
let exampleProjectId
const testProjectMatch = /^bad[^\\]+name$/
beforeEach(function (done) {
uploadExampleProject(
owner,
'test_project_with_invalid_name.zip',
(error, projectId) => {
if (error) {
return done(error)
}
exampleProjectId = projectId
done()
}
)
})
it('should set the project name from the zip contents', function (done) {
ProjectGetter.getProject(exampleProjectId, (error, project) => {
expect(error).not.to.exist
expect(project.name).to.match(testProjectMatch)
done()
})
})
})
describe('uploading an empty zipfile', function () {
let res
beforeEach(function (done) {
uploadExampleProject(
owner,
'test_project_empty.zip',
{ allowBadStatus: true },
(err, projectId, response) => {
if (err) {
return done(err)
}
res = response
done()
}
)
})
it('should fail with 422 error', function () {
expect(res.statusCode).to.equal(422)
})
})
describe('uploading a zipfile containing only empty directories', function () {
let res
beforeEach(function (done) {
uploadExampleProject(
owner,
'test_project_with_empty_folder.zip',
{ allowBadStatus: true },
(err, projectId, response) => {
if (err) {
return done(err)
}
res = response
done()
}
)
})
it('should fail with 422 error', function () {
expect(res.statusCode).to.equal(422)
})
})
describe('uploading a project with a shared top-level folder', function () {
let exampleProjectId
beforeEach(function (done) {
uploadExampleProject(
owner,
'test_project_with_shared_top_level_folder.zip',
(err, projectId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
done()
}
)
})
it('should not create the top-level folder', function (done) {
ProjectGetter.getProject(exampleProjectId, (error, project) => {
expect(error).not.to.exist
expect(project.rootFolder[0].folders.length).to.equal(0)
expect(project.rootFolder[0].docs.length).to.equal(2)
done()
})
})
})
describe('uploading a project with backslashes in the path names', function () {
let exampleProjectId
beforeEach(function (done) {
uploadExampleProject(
owner,
'test_project_with_backslash_in_filename.zip',
(err, projectId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
done()
}
)
})
it('should treat the backslash as a directory separator', function (done) {
ProjectGetter.getProject(exampleProjectId, (error, project) => {
expect(error).not.to.exist
expect(project.rootFolder[0].folders[0].name).to.equal('styles')
expect(project.rootFolder[0].folders[0].docs[0].name).to.equal('ao.sty')
done()
})
})
})
describe('uploading a project with files in different encodings', function () {
let updates
beforeEach(function (done) {
uploadExampleProject(owner, 'charsets/charsets.zip', (err, projectId) => {
if (err) {
return done(err)
}
updates = MockDocUpdaterApi.getProjectStructureUpdates(projectId)
.updates
done()
})
})
it('should correctly parse windows-1252', function () {
const update = _.find(
updates,
update => update.pathname === '/test-german-windows-1252.tex'
)
expect(update.docLines).to.contain(
'Der schnelle braune Fuchs sprang träge über den Hund.'
)
})
it('should correctly parse German utf8', function () {
const update = _.find(
updates,
update => update.pathname === '/test-german-utf8x.tex'
)
expect(update.docLines).to.contain(
'Der schnelle braune Fuchs sprang träge über den Hund.'
)
})
it('should correctly parse little-endian utf16', function () {
const update = _.find(
updates,
update => update.pathname === '/test-greek-utf16-le-bom.tex'
)
expect(update.docLines).to.contain(
'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
)
})
it('should correctly parse Greek utf8', function () {
const update = _.find(
updates,
update => update.pathname === '/test-greek-utf8x.tex'
)
expect(update.docLines).to.contain(
'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
)
})
})
describe('uploading a file', function () {
let exampleProjectId, oldVersion, rootFolderId
beforeEach(function (done) {
createExampleProject(owner, (err, projectId, folderId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
rootFolderId = folderId
MockDocUpdaterApi.reset()
ProjectGetter.getProject(projectId, (error, project) => {
if (error) {
throw error
}
oldVersion = project.version
uploadExampleFile(owner, projectId, rootFolderId, done)
})
})
})
it('should version a newly uploaded file', function (done) {
const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
exampleProjectId
)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('add-file')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/1pixel.png')
expect(update.url).to.be.a('string')
// one file upload
verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
})
it('should version a replacement file', function (done) {
MockDocUpdaterApi.reset()
uploadFile(
owner,
exampleProjectId,
rootFolderId,
'2pixel.png',
'1pixel.png',
'image/png',
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(2)
expect(updates[0].type).to.equal('rename-file')
expect(updates[0].userId).to.equal(owner._id)
expect(updates[0].pathname).to.equal('/1pixel.png')
expect(updates[0].newPathname).to.equal('')
expect(updates[1].type).to.equal('add-file')
expect(updates[1].userId).to.equal(owner._id)
expect(updates[1].pathname).to.equal('/1pixel.png')
expect(updates[1].url).to.be.a('string')
// two file uploads
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
2,
done
)
}
)
})
})
describe('moving entities', function () {
let exampleProjectId,
oldVersion,
exampleDocId,
exampleFileId,
exampleFolderId
beforeEach(function (done) {
createExampleProject(owner, (err, projectId, rootFolderId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
createExampleDoc(owner, projectId, (err, docId) => {
if (err) {
return done(err)
}
exampleDocId = docId
uploadExampleFile(owner, projectId, rootFolderId, (err, fileId) => {
if (err) {
return done(err)
}
exampleFileId = fileId
createExampleFolder(owner, projectId, (err, folderId) => {
if (err) {
return done(err)
}
exampleFolderId = folderId
ProjectGetter.getProject(projectId, (error, project) => {
if (error) {
throw error
}
oldVersion = project.version
MockDocUpdaterApi.reset()
done()
})
})
})
})
})
})
it('should version moving a doc', function (done) {
moveItem(
owner,
exampleProjectId,
'doc',
exampleDocId,
exampleFolderId,
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('rename-doc')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/new.tex')
expect(update.newPathname).to.equal('/foo/new.tex')
// 2, because it's a delete and then add
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
2,
done
)
}
)
})
it('should version moving a file', function (done) {
moveItem(
owner,
exampleProjectId,
'file',
exampleFileId,
exampleFolderId,
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('rename-file')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/1pixel.png')
expect(update.newPathname).to.equal('/foo/1pixel.png')
// 2, because it's a delete and then add
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
2,
done
)
}
)
})
it('should version moving a folder', function (done) {
moveItem(
owner,
exampleProjectId,
'doc',
exampleDocId,
exampleFolderId,
() => {
MockDocUpdaterApi.reset()
owner.request.post(
{
uri: `project/${exampleProjectId}/folder`,
json: {
name: 'bar',
},
},
(error, res, body) => {
if (error) {
throw error
}
const newFolderId = body._id
moveItem(
owner,
exampleProjectId,
'folder',
exampleFolderId,
newFolderId,
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(
exampleProjectId
)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('rename-doc')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/foo/new.tex')
expect(update.newPathname).to.equal('/bar/foo/new.tex')
// 5, because it's two file moves plus a folder
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
5,
done
)
}
)
}
)
}
)
})
})
describe('renaming entities', function () {
let exampleProjectId,
exampleDocId,
exampleFileId,
exampleFolderId,
oldVersion
beforeEach(function (done) {
createExampleProject(owner, (err, projectId, rootFolderId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
createExampleDoc(owner, projectId, (err, docId) => {
if (err) {
return done(err)
}
exampleDocId = docId
uploadExampleFile(owner, projectId, rootFolderId, (err, fileId) => {
if (err) {
return done(err)
}
exampleFileId = fileId
createExampleFolder(owner, projectId, (err, folderId) => {
if (err) {
return done(err)
}
exampleFolderId = folderId
moveItem(owner, projectId, 'doc', docId, folderId, () => {
moveItem(owner, projectId, 'file', fileId, folderId, () => {
MockDocUpdaterApi.reset()
ProjectGetter.getProject(
exampleProjectId,
(error, project) => {
if (error) {
throw error
}
oldVersion = project.version
done()
}
)
})
})
})
})
})
})
})
it('should version renaming a doc', function (done) {
renameItem(
owner,
exampleProjectId,
'Doc',
exampleDocId,
'wombat.tex',
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('rename-doc')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/foo/new.tex')
expect(update.newPathname).to.equal('/foo/wombat.tex')
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
1,
done
)
}
)
})
it('should version renaming a file', function (done) {
renameItem(
owner,
exampleProjectId,
'file',
exampleFileId,
'potato.png',
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('rename-file')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/foo/1pixel.png')
expect(update.newPathname).to.equal('/foo/potato.png')
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
1,
done
)
}
)
})
it('should version renaming a folder', function (done) {
renameItem(
owner,
exampleProjectId,
'folder',
exampleFolderId,
'giraffe',
() => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(2)
expect(updates[0].type).to.equal('rename-doc')
expect(updates[0].userId).to.equal(owner._id)
expect(updates[0].pathname).to.equal('/foo/new.tex')
expect(updates[0].newPathname).to.equal('/giraffe/new.tex')
expect(updates[1].type).to.equal('rename-file')
expect(updates[1].userId).to.equal(owner._id)
expect(updates[1].pathname).to.equal('/foo/1pixel.png')
expect(updates[1].newPathname).to.equal('/giraffe/1pixel.png')
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
1,
done
)
}
)
})
})
describe('deleting entities', function () {
let exampleProjectId, oldVersion, exampleFolderId
beforeEach(function (done) {
createExampleProject(owner, (err, projectId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
createExampleFolder(owner, exampleProjectId, (err, folderId) => {
if (err) {
return done(err)
}
exampleFolderId = folderId
createExampleDoc(owner, projectId, (err, docId) => {
if (err) {
return done(err)
}
uploadExampleFile(owner, projectId, folderId, (err, fileId) => {
if (err) {
return done(err)
}
moveItem(owner, projectId, 'doc', docId, folderId, () => {
moveItem(owner, projectId, 'file', fileId, folderId, () => {
MockDocUpdaterApi.reset()
ProjectGetter.getProject(
exampleProjectId,
(error, project) => {
if (error) {
throw error
}
oldVersion = project.version
done()
}
)
})
})
})
})
})
})
})
it('should version deleting a folder', function (done) {
deleteItem(owner, exampleProjectId, 'folder', exampleFolderId, () => {
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(2)
expect(updates[0].type).to.equal('rename-doc')
expect(updates[0].userId).to.equal(owner._id)
expect(updates[0].pathname).to.equal('/foo/new.tex')
expect(updates[0].newPathname).to.equal('')
expect(updates[1].type).to.equal('rename-file')
expect(updates[1].userId).to.equal(owner._id)
expect(updates[1].pathname).to.equal('/foo/1pixel.png')
expect(updates[1].newPathname).to.equal('')
verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
})
})
})
describe('deleting docs', function () {
beforeEach(function (done) {
createExampleProject(owner, (err, projectId) => {
if (err) {
return done(err)
}
this.exampleProjectId = projectId
createExampleFolder(owner, projectId, (err, folderId) => {
if (err) {
return done(err)
}
this.exampleFolderId = folderId
createExampleDoc(owner, projectId, (err, docId) => {
if (err) {
return done(err)
}
this.exampleDocId = docId
MockDocUpdaterApi.reset()
ProjectGetter.getProject(
this.exampleProjectId,
(error, project) => {
if (error) {
throw error
}
this.project0 = project
done()
}
)
})
})
})
})
it('should pass the doc name to docstore', function (done) {
deleteItem(
owner,
this.exampleProjectId,
'doc',
this.exampleDocId,
error => {
if (error) return done(error)
expect(
MockDocStoreApi.getDeletedDocs(this.exampleProjectId)
).to.deep.equal([{ _id: this.exampleDocId, name: 'new.tex' }])
done()
}
)
})
describe('when rootDoc_id matches doc being deleted', function () {
beforeEach(function (done) {
Project.updateOne(
{ _id: this.exampleProjectId },
{ $set: { rootDoc_id: this.exampleDocId } },
done
)
})
it('should clear rootDoc_id', function (done) {
deleteItem(
owner,
this.exampleProjectId,
'doc',
this.exampleDocId,
() => {
ProjectGetter.getProject(
this.exampleProjectId,
(error, project) => {
if (error) {
throw error
}
expect(project.rootDoc_id).to.be.undefined
done()
}
)
}
)
})
})
describe('when rootDoc_id does not match doc being deleted', function () {
beforeEach(function (done) {
this.exampleRootDocId = new ObjectId()
Project.updateOne(
{ _id: this.exampleProjectId },
{ $set: { rootDoc_id: this.exampleRootDocId } },
done
)
})
it('should not clear rootDoc_id', function (done) {
deleteItem(
owner,
this.exampleProjectId,
'doc',
this.exampleDocId,
() => {
ProjectGetter.getProject(
this.exampleProjectId,
(error, project) => {
if (error) {
throw error
}
expect(project.rootDoc_id.toString()).to.equal(
this.exampleRootDocId.toString()
)
done()
}
)
}
)
})
})
})
describe('tpds', function () {
let projectName, exampleProjectId, oldVersion, rootFolderId
beforeEach(function (done) {
projectName = `tpds-project-${new ObjectId().toString()}`
owner.createProject(projectName, (error, projectId) => {
if (error) {
throw error
}
exampleProjectId = projectId
mkdirp(Settings.path.dumpFolder, () => {
ProjectGetter.getProject(exampleProjectId, (error, project) => {
if (error) {
throw error
}
MockDocUpdaterApi.reset()
rootFolderId = project.rootFolder[0]._id.toString()
oldVersion = project.version
done()
})
})
})
})
it('should version adding a doc', function (done) {
const texFile = fs.createReadStream(
Path.resolve(Path.join(__dirname, '..', 'files', 'test.tex'))
)
const req = owner.request.post({
uri: `/user/${owner._id}/update/${projectName}/test.tex`,
auth: {
user: _.keys(Settings.httpAuthUsers)[0],
pass: _.values(Settings.httpAuthUsers)[0],
sendImmediately: true,
},
})
texFile.on('error', err => {
throw err
})
req.on('error', err => {
throw err
})
req.on('response', res => {
if (res.statusCode < 200 || res.statusCode >= 300) {
throw new Error(`failed to upload file ${res.statusCode}`)
}
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('add-doc')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/test.tex')
expect(update.docLines).to.equal('Test')
verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
})
texFile.pipe(req)
})
it('should version adding a new file', function (done) {
const imageFile = fs.createReadStream(
Path.resolve(Path.join(__dirname, '..', 'files', '1pixel.png'))
)
const req = owner.request.post({
uri: `/user/${owner._id}/update/${projectName}/1pixel.png`,
auth: {
user: _.keys(Settings.httpAuthUsers)[0],
pass: _.values(Settings.httpAuthUsers)[0],
sendImmediately: true,
},
})
imageFile.on('error', err => {
throw err
})
req.on('error', err => {
throw err
})
req.on('response', res => {
if (res.statusCode < 200 || res.statusCode >= 300) {
throw new Error(`failed to upload file ${res.statusCode}`)
}
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('add-file')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/1pixel.png')
expect(update.url).to.be.a('string')
verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
})
imageFile.pipe(req)
})
describe('when there are files in the project', function () {
beforeEach(function (done) {
uploadExampleFile(owner, exampleProjectId, rootFolderId, () => {
createExampleDoc(owner, exampleProjectId, () => {
ProjectGetter.getProject(exampleProjectId, (error, project) => {
if (error) {
throw error
}
MockDocUpdaterApi.reset()
oldVersion = project.version
done()
})
})
})
})
it('should version replacing a file', function (done) {
const imageFile = fs.createReadStream(
Path.resolve(Path.join(__dirname, '..', 'files', '2pixel.png'))
)
const req = owner.request.post({
uri: `/user/${owner._id}/update/${projectName}/1pixel.png`,
auth: {
user: _.keys(Settings.httpAuthUsers)[0],
pass: _.values(Settings.httpAuthUsers)[0],
sendImmediately: true,
},
})
imageFile.on('error', err => {
throw err
})
req.on('error', err => {
throw err
})
req.on('response', res => {
if (res.statusCode < 200 || res.statusCode >= 300) {
throw new Error(`failed to upload file ${res.statusCode}`)
}
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(2)
expect(updates[0].type).to.equal('rename-file')
expect(updates[0].userId).to.equal(owner._id)
expect(updates[0].pathname).to.equal('/1pixel.png')
expect(updates[0].newPathname).to.equal('')
expect(updates[1].type).to.equal('add-file')
expect(updates[1].userId).to.equal(owner._id)
expect(updates[1].pathname).to.equal('/1pixel.png')
expect(updates[1].url).to.be.a('string')
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
1,
done
)
})
imageFile.pipe(req)
})
it('should version deleting a doc', function (done) {
owner.request.delete(
{
uri: `/user/${owner._id}/update/${projectName}/new.tex`,
auth: {
user: _.keys(Settings.httpAuthUsers)[0],
pass: _.values(Settings.httpAuthUsers)[0],
sendImmediately: true,
},
},
(error, res) => {
if (error) {
throw error
}
if (res.statusCode < 200 || res.statusCode >= 300) {
throw new Error(`failed to delete doc ${res.statusCode}`)
}
const {
updates,
version,
} = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('rename-doc')
expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/new.tex')
expect(update.newPathname).to.equal('')
verifyVersionIncremented(
exampleProjectId,
oldVersion,
version,
1,
done
)
}
)
})
})
})
describe('uploading a document', function () {
let exampleProjectId, rootFolderId
beforeEach(function (done) {
createExampleProject(owner, (err, projectId, folderId) => {
if (err) {
return done(err)
}
exampleProjectId = projectId
rootFolderId = folderId
MockDocUpdaterApi.reset()
done()
})
})
describe('with an unusual character set', function () {
it('should correctly handle utf16-le data', function (done) {
uploadFile(
owner,
exampleProjectId,
rootFolderId,
'charsets/test-greek-utf16-le-bom.tex',
'test-greek-utf16-le-bom.tex',
'text/x-tex',
() => {
const { updates } = MockDocUpdaterApi.getProjectStructureUpdates(
exampleProjectId
)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('add-doc')
expect(update.pathname).to.equal('/test-greek-utf16-le-bom.tex')
expect(update.docLines).to.contain(
'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
)
done()
}
)
})
it('should correctly handle windows1252/iso-8859-1/latin1 data', function (done) {
uploadFile(
owner,
exampleProjectId,
rootFolderId,
'charsets/test-german-windows-1252.tex',
'test-german-windows-1252.tex',
'text/x-tex',
() => {
const { updates } = MockDocUpdaterApi.getProjectStructureUpdates(
exampleProjectId
)
expect(updates.length).to.equal(1)
const update = updates[0]
expect(update.type).to.equal('add-doc')
expect(update.pathname).to.equal('/test-german-windows-1252.tex')
expect(update.docLines).to.contain(
'Der schnelle braune Fuchs sprang träge über den Hund.'
)
done()
}
)
})
})
})
})