Convert tests to ESM
GitOrigin-RevId: 20585e01dee90e691476a0d47fd5c63b0412e4a6
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,41 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
const { ObjectId } = require('mongodb-legacy')
|
||||
const Errors = require('../../../../app/src/Features/Errors/Errors')
|
||||
const ProjectHelper = require('../../../../app/src/Features/Project/ProjectHelper')
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
||||
import ProjectHelper from '../../../../app/src/Features/Project/ProjectHelper.js'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
const MODULE_PATH = '../../../../app/src/Features/Project/ProjectDetailsHandler'
|
||||
|
||||
vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
||||
vi.importActual('../../../../app/src/Features/Errors/Errors.js')
|
||||
)
|
||||
|
||||
describe('ProjectDetailsHandler', function () {
|
||||
beforeEach(function () {
|
||||
this.user = {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.user = {
|
||||
_id: new ObjectId(),
|
||||
email: 'user@example.com',
|
||||
features: 'mock-features',
|
||||
}
|
||||
this.collaborator = {
|
||||
ctx.collaborator = {
|
||||
_id: new ObjectId(),
|
||||
email: 'collaborator@example.com',
|
||||
}
|
||||
this.project = {
|
||||
ctx.project = {
|
||||
_id: new ObjectId(),
|
||||
name: 'project',
|
||||
description: 'this is a great project',
|
||||
something: 'should not exist',
|
||||
compiler: 'latexxxxxx',
|
||||
owner_ref: this.user._id,
|
||||
collaberator_refs: [this.collaborator._id],
|
||||
owner_ref: ctx.user._id,
|
||||
collaberator_refs: [ctx.collaborator._id],
|
||||
}
|
||||
this.ProjectGetter = {
|
||||
ctx.ProjectGetter = {
|
||||
promises: {
|
||||
getProjectWithoutDocLines: sinon.stub().resolves(this.project),
|
||||
getProject: sinon.stub().resolves(this.project),
|
||||
getProjectWithoutDocLines: sinon.stub().resolves(ctx.project),
|
||||
getProject: sinon.stub().resolves(ctx.project),
|
||||
findAllUsersProjects: sinon.stub().resolves({
|
||||
owned: [],
|
||||
readAndWrite: [],
|
||||
@@ -40,200 +45,221 @@ describe('ProjectDetailsHandler', function () {
|
||||
}),
|
||||
},
|
||||
}
|
||||
this.ProjectModelUpdateQuery = {
|
||||
ctx.ProjectModelUpdateQuery = {
|
||||
exec: sinon.stub().resolves(),
|
||||
}
|
||||
this.ProjectModel = {
|
||||
updateOne: sinon.stub().returns(this.ProjectModelUpdateQuery),
|
||||
ctx.ProjectModel = {
|
||||
updateOne: sinon.stub().returns(ctx.ProjectModelUpdateQuery),
|
||||
}
|
||||
this.UserGetter = {
|
||||
ctx.UserGetter = {
|
||||
promises: {
|
||||
getUser: sinon.stub().resolves(this.user),
|
||||
getUser: sinon.stub().resolves(ctx.user),
|
||||
},
|
||||
}
|
||||
this.TpdsUpdateSender = {
|
||||
ctx.TpdsUpdateSender = {
|
||||
promises: {
|
||||
moveEntity: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
this.TokenGenerator = {
|
||||
ctx.TokenGenerator = {
|
||||
readAndWriteToken: sinon.stub(),
|
||||
promises: {
|
||||
generateUniqueReadOnlyToken: sinon.stub(),
|
||||
},
|
||||
}
|
||||
this.settings = {
|
||||
ctx.settings = {
|
||||
defaultFeatures: 'default-features',
|
||||
}
|
||||
this.handler = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'./ProjectHelper': ProjectHelper,
|
||||
'./ProjectGetter': this.ProjectGetter,
|
||||
'../../models/Project': {
|
||||
Project: this.ProjectModel,
|
||||
},
|
||||
'../User/UserGetter': this.UserGetter,
|
||||
'../ThirdPartyDataStore/TpdsUpdateSender': this.TpdsUpdateSender,
|
||||
'../TokenGenerator/TokenGenerator': this.TokenGenerator,
|
||||
'@overleaf/settings': this.settings,
|
||||
},
|
||||
})
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectHelper', () => ({
|
||||
default: ProjectHelper,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectGetter', () => ({
|
||||
default: ctx.ProjectGetter,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/Project', () => ({
|
||||
Project: ctx.ProjectModel,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
|
||||
default: ctx.UserGetter,
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/ThirdPartyDataStore/TpdsUpdateSender',
|
||||
() => ({
|
||||
default: ctx.TpdsUpdateSender,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/TokenGenerator/TokenGenerator',
|
||||
() => ({
|
||||
default: ctx.TokenGenerator,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: ctx.settings,
|
||||
}))
|
||||
|
||||
ctx.handler = (await import(MODULE_PATH)).default
|
||||
})
|
||||
|
||||
describe('getDetails', function () {
|
||||
it('should find the project and owner', async function () {
|
||||
const details = await this.handler.promises.getDetails(this.project._id)
|
||||
details.name.should.equal(this.project.name)
|
||||
details.description.should.equal(this.project.description)
|
||||
details.compiler.should.equal(this.project.compiler)
|
||||
details.features.should.equal(this.user.features)
|
||||
it('should find the project and owner', async function (ctx) {
|
||||
const details = await ctx.handler.promises.getDetails(ctx.project._id)
|
||||
details.name.should.equal(ctx.project.name)
|
||||
details.description.should.equal(ctx.project.description)
|
||||
details.compiler.should.equal(ctx.project.compiler)
|
||||
details.features.should.equal(ctx.user.features)
|
||||
expect(details.something).to.be.undefined
|
||||
})
|
||||
|
||||
it('should find overleaf metadata if it exists', async function () {
|
||||
this.project.overleaf = { id: 'id' }
|
||||
const details = await this.handler.promises.getDetails(this.project._id)
|
||||
details.overleaf.should.equal(this.project.overleaf)
|
||||
it('should find overleaf metadata if it exists', async function (ctx) {
|
||||
ctx.project.overleaf = { id: 'id' }
|
||||
const details = await ctx.handler.promises.getDetails(ctx.project._id)
|
||||
details.overleaf.should.equal(ctx.project.overleaf)
|
||||
expect(details.something).to.be.undefined
|
||||
})
|
||||
|
||||
it('should return an error for a non-existent project', async function () {
|
||||
this.ProjectGetter.promises.getProject.resolves(null)
|
||||
it('should return an error for a non-existent project', async function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.resolves(null)
|
||||
await expect(
|
||||
this.handler.promises.getDetails('0123456789012345678901234')
|
||||
ctx.handler.promises.getDetails('0123456789012345678901234')
|
||||
).to.be.rejectedWith(Errors.NotFoundError)
|
||||
})
|
||||
|
||||
it('should return the default features if no owner found', async function () {
|
||||
this.UserGetter.promises.getUser.resolves(null)
|
||||
const details = await this.handler.promises.getDetails(this.project._id)
|
||||
details.features.should.equal(this.settings.defaultFeatures)
|
||||
it('should return the default features if no owner found', async function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.resolves(null)
|
||||
const details = await ctx.handler.promises.getDetails(ctx.project._id)
|
||||
details.features.should.equal(ctx.settings.defaultFeatures)
|
||||
})
|
||||
|
||||
it('should rethrow any error', async function () {
|
||||
this.ProjectGetter.promises.getProject.rejects(new Error('boom'))
|
||||
await expect(this.handler.promises.getDetails(this.project._id)).to.be
|
||||
it('should rethrow any error', async function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.rejects(new Error('boom'))
|
||||
await expect(ctx.handler.promises.getDetails(ctx.project._id)).to.be
|
||||
.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('getProjectDescription', function () {
|
||||
it('should make a call to mongo just for the description', async function () {
|
||||
this.ProjectGetter.promises.getProject.resolves()
|
||||
await this.handler.promises.getProjectDescription(this.project._id)
|
||||
expect(this.ProjectGetter.promises.getProject).to.have.been.calledWith(
|
||||
this.project._id,
|
||||
it('should make a call to mongo just for the description', async function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.resolves()
|
||||
await ctx.handler.promises.getProjectDescription(ctx.project._id)
|
||||
expect(ctx.ProjectGetter.promises.getProject).to.have.been.calledWith(
|
||||
ctx.project._id,
|
||||
{ description: true }
|
||||
)
|
||||
})
|
||||
|
||||
it('should return what the mongo call returns', async function () {
|
||||
it('should return what the mongo call returns', async function (ctx) {
|
||||
const expectedDescription = 'cool project'
|
||||
this.ProjectGetter.promises.getProject.resolves({
|
||||
ctx.ProjectGetter.promises.getProject.resolves({
|
||||
description: expectedDescription,
|
||||
})
|
||||
const description = await this.handler.promises.getProjectDescription(
|
||||
this.project._id
|
||||
const description = await ctx.handler.promises.getProjectDescription(
|
||||
ctx.project._id
|
||||
)
|
||||
expect(description).to.equal(expectedDescription)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setProjectDescription', function () {
|
||||
beforeEach(function () {
|
||||
this.description = 'updated teh description'
|
||||
beforeEach(function (ctx) {
|
||||
ctx.description = 'updated teh description'
|
||||
})
|
||||
|
||||
it('should update the project detials', async function () {
|
||||
await this.handler.promises.setProjectDescription(
|
||||
this.project._id,
|
||||
this.description
|
||||
it('should update the project detials', async function (ctx) {
|
||||
await ctx.handler.promises.setProjectDescription(
|
||||
ctx.project._id,
|
||||
ctx.description
|
||||
)
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: this.project._id },
|
||||
{ description: this.description }
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: ctx.project._id },
|
||||
{ description: ctx.description }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('renameProject', function () {
|
||||
beforeEach(function () {
|
||||
this.newName = 'new name here'
|
||||
beforeEach(function (ctx) {
|
||||
ctx.newName = 'new name here'
|
||||
})
|
||||
|
||||
it('should update the project with the new name', async function () {
|
||||
await this.handler.promises.renameProject(this.project._id, this.newName)
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: this.project._id },
|
||||
{ name: this.newName }
|
||||
it('should update the project with the new name', async function (ctx) {
|
||||
await ctx.handler.promises.renameProject(ctx.project._id, ctx.newName)
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: ctx.project._id },
|
||||
{ name: ctx.newName }
|
||||
)
|
||||
})
|
||||
|
||||
it('should tell the TpdsUpdateSender', async function () {
|
||||
await this.handler.promises.renameProject(this.project._id, this.newName)
|
||||
expect(this.TpdsUpdateSender.promises.moveEntity).to.have.been.calledWith(
|
||||
{
|
||||
projectId: this.project._id,
|
||||
projectName: this.project.name,
|
||||
newProjectName: this.newName,
|
||||
}
|
||||
)
|
||||
it('should tell the TpdsUpdateSender', async function (ctx) {
|
||||
await ctx.handler.promises.renameProject(ctx.project._id, ctx.newName)
|
||||
expect(ctx.TpdsUpdateSender.promises.moveEntity).to.have.been.calledWith({
|
||||
projectId: ctx.project._id,
|
||||
projectName: ctx.project.name,
|
||||
newProjectName: ctx.newName,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not do anything with an invalid name', async function () {
|
||||
await expect(this.handler.promises.renameProject(this.project._id)).to.be
|
||||
it('should not do anything with an invalid name', async function (ctx) {
|
||||
await expect(ctx.handler.promises.renameProject(ctx.project._id)).to.be
|
||||
.rejected
|
||||
expect(this.TpdsUpdateSender.promises.moveEntity).not.to.have.been.called
|
||||
expect(this.ProjectModel.updateOne).not.to.have.been.called
|
||||
expect(ctx.TpdsUpdateSender.promises.moveEntity).not.to.have.been.called
|
||||
expect(ctx.ProjectModel.updateOne).not.to.have.been.called
|
||||
})
|
||||
|
||||
it('should trim whitespace around name', async function () {
|
||||
await this.handler.promises.renameProject(
|
||||
this.project._id,
|
||||
` ${this.newName} `
|
||||
it('should trim whitespace around name', async function (ctx) {
|
||||
await ctx.handler.promises.renameProject(
|
||||
ctx.project._id,
|
||||
` ${ctx.newName} `
|
||||
)
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: this.project._id },
|
||||
{ name: this.newName }
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: ctx.project._id },
|
||||
{ name: ctx.newName }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateProjectName', function () {
|
||||
it('should reject undefined names', async function () {
|
||||
await expect(this.handler.promises.validateProjectName(undefined)).to.be
|
||||
it('should reject undefined names', async function (ctx) {
|
||||
await expect(ctx.handler.promises.validateProjectName(undefined)).to.be
|
||||
.rejected
|
||||
})
|
||||
|
||||
it('should reject empty names', async function () {
|
||||
await expect(this.handler.promises.validateProjectName('')).to.be.rejected
|
||||
it('should reject empty names', async function (ctx) {
|
||||
await expect(ctx.handler.promises.validateProjectName('')).to.be.rejected
|
||||
})
|
||||
|
||||
it('should reject names with /s', async function () {
|
||||
await expect(this.handler.promises.validateProjectName('foo/bar')).to.be
|
||||
it('should reject names with /s', async function (ctx) {
|
||||
await expect(ctx.handler.promises.validateProjectName('foo/bar')).to.be
|
||||
.rejected
|
||||
})
|
||||
|
||||
it('should reject names with \\s', async function () {
|
||||
await expect(this.handler.promises.validateProjectName('foo\\bar')).to.be
|
||||
it('should reject names with \\s', async function (ctx) {
|
||||
await expect(ctx.handler.promises.validateProjectName('foo\\bar')).to.be
|
||||
.rejected
|
||||
})
|
||||
|
||||
it('should reject long names', async function () {
|
||||
await expect(this.handler.promises.validateProjectName('a'.repeat(1000)))
|
||||
it('should reject long names', async function (ctx) {
|
||||
await expect(ctx.handler.promises.validateProjectName('a'.repeat(1000)))
|
||||
.to.be.rejected
|
||||
})
|
||||
|
||||
it('should accept normal names', async function () {
|
||||
await expect(this.handler.promises.validateProjectName('foobar')).to.be
|
||||
it('should accept normal names', async function (ctx) {
|
||||
await expect(ctx.handler.promises.validateProjectName('foobar')).to.be
|
||||
.fulfilled
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateUniqueName', function () {
|
||||
// actually testing `ProjectHelper.promises.ensureNameIsUnique()`
|
||||
beforeEach(function () {
|
||||
this.longName = 'x'.repeat(this.handler.MAX_PROJECT_NAME_LENGTH - 5)
|
||||
beforeEach(function (ctx) {
|
||||
ctx.longName = 'x'.repeat(ctx.handler.MAX_PROJECT_NAME_LENGTH - 5)
|
||||
const usersProjects = {
|
||||
owned: [
|
||||
{ _id: 1, name: 'name' },
|
||||
@@ -290,116 +316,116 @@ describe('ProjectDetailsHandler', function () {
|
||||
tokenReadOnly: [
|
||||
{ _id: 10, name: 'name5' },
|
||||
{ _id: 11, name: 'name55' },
|
||||
{ _id: 12, name: this.longName },
|
||||
{ _id: 12, name: ctx.longName },
|
||||
],
|
||||
}
|
||||
this.ProjectGetter.promises.findAllUsersProjects.resolves(usersProjects)
|
||||
ctx.ProjectGetter.promises.findAllUsersProjects.resolves(usersProjects)
|
||||
})
|
||||
|
||||
it('should leave a unique name unchanged', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should leave a unique name unchanged', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'unique-name',
|
||||
['-test-suffix']
|
||||
)
|
||||
expect(name).to.equal('unique-name')
|
||||
})
|
||||
|
||||
it('should append a suffix to an existing name', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should append a suffix to an existing name', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'name1',
|
||||
['-test-suffix']
|
||||
)
|
||||
expect(name).to.equal('name1-test-suffix')
|
||||
})
|
||||
|
||||
it('should fallback to a second suffix when needed', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should fallback to a second suffix when needed', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'name1',
|
||||
['1', '-test-suffix']
|
||||
)
|
||||
expect(name).to.equal('name1-test-suffix')
|
||||
})
|
||||
|
||||
it('should truncate the name when append a suffix if the result is too long', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
this.longName,
|
||||
it('should truncate the name when append a suffix if the result is too long', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
ctx.longName,
|
||||
['-test-suffix']
|
||||
)
|
||||
expect(name).to.equal(
|
||||
this.longName.substr(0, this.handler.MAX_PROJECT_NAME_LENGTH - 12) +
|
||||
ctx.longName.substr(0, ctx.handler.MAX_PROJECT_NAME_LENGTH - 12) +
|
||||
'-test-suffix'
|
||||
)
|
||||
})
|
||||
|
||||
it('should use a numeric index if no suffix is supplied', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should use a numeric index if no suffix is supplied', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'name1',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('name1 (1)')
|
||||
})
|
||||
|
||||
it('should use a numeric index if all suffixes are exhausted', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should use a numeric index if all suffixes are exhausted', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'name',
|
||||
['1', '11']
|
||||
)
|
||||
expect(name).to.equal('name (1)')
|
||||
})
|
||||
|
||||
it('should find the next lowest available numeric index for the base name', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should find the next lowest available numeric index for the base name', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'numeric',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('numeric (21)')
|
||||
})
|
||||
|
||||
it('should not find a numeric index lower than the one already present', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should not find a numeric index lower than the one already present', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'numeric (31)',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('numeric (41)')
|
||||
})
|
||||
|
||||
it('should handle years in name', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should handle years in name', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'unique-name (2021)',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('unique-name (2021)')
|
||||
})
|
||||
|
||||
it('should handle duplicating with year in name', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should handle duplicating with year in name', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'Yearbook (2021)',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('Yearbook (2021) (2)')
|
||||
})
|
||||
describe('title with that causes invalid regex', function () {
|
||||
it('should create the project with a suffix when project name exists', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should create the project with a suffix when project name exists', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'Resume (2020',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('Resume (2020 (1)')
|
||||
})
|
||||
it('should create the project with the provided name', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should create the project with the provided name', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'Yearbook (2021',
|
||||
[]
|
||||
)
|
||||
@@ -409,18 +435,18 @@ describe('ProjectDetailsHandler', function () {
|
||||
|
||||
describe('numeric index is already present', function () {
|
||||
describe('when there is 1 project "x (2)"', function () {
|
||||
beforeEach(function () {
|
||||
beforeEach(function (ctx) {
|
||||
const usersProjects = {
|
||||
owned: [{ _id: 1, name: 'x (2)' }],
|
||||
}
|
||||
this.ProjectGetter.promises.findAllUsersProjects.resolves(
|
||||
ctx.ProjectGetter.promises.findAllUsersProjects.resolves(
|
||||
usersProjects
|
||||
)
|
||||
})
|
||||
|
||||
it('should produce "x (3)" uploading a zip with name "x (2)"', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should produce "x (3)" uploading a zip with name "x (2)"', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'x (2)',
|
||||
[]
|
||||
)
|
||||
@@ -429,21 +455,21 @@ describe('ProjectDetailsHandler', function () {
|
||||
})
|
||||
|
||||
describe('when there are 2 projects "x (2)" and "x (3)"', function () {
|
||||
beforeEach(function () {
|
||||
beforeEach(function (ctx) {
|
||||
const usersProjects = {
|
||||
owned: [
|
||||
{ _id: 1, name: 'x (2)' },
|
||||
{ _id: 2, name: 'x (3)' },
|
||||
],
|
||||
}
|
||||
this.ProjectGetter.promises.findAllUsersProjects.resolves(
|
||||
ctx.ProjectGetter.promises.findAllUsersProjects.resolves(
|
||||
usersProjects
|
||||
)
|
||||
})
|
||||
|
||||
it('should produce "x (4)" when uploading a zip with name "x (2)"', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should produce "x (4)" when uploading a zip with name "x (2)"', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'x (2)',
|
||||
[]
|
||||
)
|
||||
@@ -452,30 +478,30 @@ describe('ProjectDetailsHandler', function () {
|
||||
})
|
||||
|
||||
describe('when there are 2 projects "x (2)" and "x (4)"', function () {
|
||||
beforeEach(function () {
|
||||
beforeEach(function (ctx) {
|
||||
const usersProjects = {
|
||||
owned: [
|
||||
{ _id: 1, name: 'x (2)' },
|
||||
{ _id: 2, name: 'x (4)' },
|
||||
],
|
||||
}
|
||||
this.ProjectGetter.promises.findAllUsersProjects.resolves(
|
||||
ctx.ProjectGetter.promises.findAllUsersProjects.resolves(
|
||||
usersProjects
|
||||
)
|
||||
})
|
||||
|
||||
it('should produce "x (3)" when uploading a zip with name "x (2)"', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should produce "x (3)" when uploading a zip with name "x (2)"', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'x (2)',
|
||||
[]
|
||||
)
|
||||
expect(name).to.equal('x (3)')
|
||||
})
|
||||
|
||||
it('should produce "x (5)" when uploading a zip with name "x (4)"', async function () {
|
||||
const name = await this.handler.promises.generateUniqueName(
|
||||
this.user._id,
|
||||
it('should produce "x (5)" when uploading a zip with name "x (4)"', async function (ctx) {
|
||||
const name = await ctx.handler.promises.generateUniqueName(
|
||||
ctx.user._id,
|
||||
'x (4)',
|
||||
[]
|
||||
)
|
||||
@@ -486,70 +512,70 @@ describe('ProjectDetailsHandler', function () {
|
||||
})
|
||||
|
||||
describe('fixProjectName', function () {
|
||||
it('should change empty names to Untitled', function () {
|
||||
expect(this.handler.fixProjectName('')).to.equal('Untitled')
|
||||
it('should change empty names to Untitled', function (ctx) {
|
||||
expect(ctx.handler.fixProjectName('')).to.equal('Untitled')
|
||||
})
|
||||
|
||||
it('should replace / with -', function () {
|
||||
expect(this.handler.fixProjectName('foo/bar')).to.equal('foo-bar')
|
||||
it('should replace / with -', function (ctx) {
|
||||
expect(ctx.handler.fixProjectName('foo/bar')).to.equal('foo-bar')
|
||||
})
|
||||
|
||||
it("should replace \\ with ''", function () {
|
||||
expect(this.handler.fixProjectName('foo \\ bar')).to.equal('foo bar')
|
||||
it("should replace \\ with ''", function (ctx) {
|
||||
expect(ctx.handler.fixProjectName('foo \\ bar')).to.equal('foo bar')
|
||||
})
|
||||
|
||||
it('should truncate long names', function () {
|
||||
expect(this.handler.fixProjectName('a'.repeat(1000))).to.equal(
|
||||
it('should truncate long names', function (ctx) {
|
||||
expect(ctx.handler.fixProjectName('a'.repeat(1000))).to.equal(
|
||||
'a'.repeat(150)
|
||||
)
|
||||
})
|
||||
|
||||
it('should accept normal names', function () {
|
||||
expect(this.handler.fixProjectName('foobar')).to.equal('foobar')
|
||||
it('should accept normal names', function (ctx) {
|
||||
expect(ctx.handler.fixProjectName('foobar')).to.equal('foobar')
|
||||
})
|
||||
|
||||
it('should trim name after truncation', function () {
|
||||
expect(this.handler.fixProjectName('a'.repeat(149) + ' a')).to.equal(
|
||||
it('should trim name after truncation', function (ctx) {
|
||||
expect(ctx.handler.fixProjectName('a'.repeat(149) + ' a')).to.equal(
|
||||
'a'.repeat(149)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setPublicAccessLevel', function () {
|
||||
beforeEach(function () {
|
||||
this.accessLevel = 'tokenBased'
|
||||
beforeEach(function (ctx) {
|
||||
ctx.accessLevel = 'tokenBased'
|
||||
})
|
||||
|
||||
it('should update the project with the new level', async function () {
|
||||
await this.handler.promises.setPublicAccessLevel(
|
||||
this.project._id,
|
||||
this.accessLevel
|
||||
it('should update the project with the new level', async function (ctx) {
|
||||
await ctx.handler.promises.setPublicAccessLevel(
|
||||
ctx.project._id,
|
||||
ctx.accessLevel
|
||||
)
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: this.project._id },
|
||||
{ publicAccesLevel: this.accessLevel }
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: ctx.project._id },
|
||||
{ publicAccesLevel: ctx.accessLevel }
|
||||
)
|
||||
})
|
||||
|
||||
it('should not produce an error', async function () {
|
||||
it('should not produce an error', async function (ctx) {
|
||||
await expect(
|
||||
this.handler.promises.setPublicAccessLevel(
|
||||
this.project._id,
|
||||
this.accessLevel
|
||||
ctx.handler.promises.setPublicAccessLevel(
|
||||
ctx.project._id,
|
||||
ctx.accessLevel
|
||||
)
|
||||
).to.be.fulfilled
|
||||
})
|
||||
|
||||
describe('when update produces an error', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectModelUpdateQuery.exec.rejects(new Error('woops'))
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectModelUpdateQuery.exec.rejects(new Error('woops'))
|
||||
})
|
||||
|
||||
it('should produce an error', async function () {
|
||||
it('should produce an error', async function (ctx) {
|
||||
await expect(
|
||||
this.handler.promises.setPublicAccessLevel(
|
||||
this.project._id,
|
||||
this.accessLevel
|
||||
ctx.handler.promises.setPublicAccessLevel(
|
||||
ctx.project._id,
|
||||
ctx.accessLevel
|
||||
)
|
||||
).to.be.rejected
|
||||
})
|
||||
@@ -558,76 +584,76 @@ describe('ProjectDetailsHandler', function () {
|
||||
|
||||
describe('ensureTokensArePresent', function () {
|
||||
describe('when the project has tokens', function () {
|
||||
beforeEach(function () {
|
||||
this.project = {
|
||||
_id: this.project._id,
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project = {
|
||||
_id: ctx.project._id,
|
||||
tokens: {
|
||||
readOnly: 'aaa',
|
||||
readAndWrite: '42bbb',
|
||||
readAndWritePrefix: '42',
|
||||
},
|
||||
}
|
||||
this.ProjectGetter.promises.getProject.resolves(this.project)
|
||||
ctx.ProjectGetter.promises.getProject.resolves(ctx.project)
|
||||
})
|
||||
|
||||
it('should get the project', async function () {
|
||||
await this.handler.promises.ensureTokensArePresent(this.project._id)
|
||||
expect(this.ProjectGetter.promises.getProject).to.have.been.calledOnce
|
||||
expect(this.ProjectGetter.promises.getProject).to.have.been.calledWith(
|
||||
this.project._id,
|
||||
it('should get the project', async function (ctx) {
|
||||
await ctx.handler.promises.ensureTokensArePresent(ctx.project._id)
|
||||
expect(ctx.ProjectGetter.promises.getProject).to.have.been.calledOnce
|
||||
expect(ctx.ProjectGetter.promises.getProject).to.have.been.calledWith(
|
||||
ctx.project._id,
|
||||
{
|
||||
tokens: 1,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should not update the project with new tokens', async function () {
|
||||
await this.handler.promises.ensureTokensArePresent(this.project._id)
|
||||
expect(this.ProjectModel.updateOne).not.to.have.been.called
|
||||
it('should not update the project with new tokens', async function (ctx) {
|
||||
await ctx.handler.promises.ensureTokensArePresent(ctx.project._id)
|
||||
expect(ctx.ProjectModel.updateOne).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('when tokens are missing', function () {
|
||||
beforeEach(function () {
|
||||
this.project = { _id: this.project._id }
|
||||
this.ProjectGetter.promises.getProject.resolves(this.project)
|
||||
this.readOnlyToken = 'abc'
|
||||
this.readAndWriteToken = '42def'
|
||||
this.readAndWriteTokenPrefix = '42'
|
||||
this.TokenGenerator.promises.generateUniqueReadOnlyToken.resolves(
|
||||
this.readOnlyToken
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project = { _id: ctx.project._id }
|
||||
ctx.ProjectGetter.promises.getProject.resolves(ctx.project)
|
||||
ctx.readOnlyToken = 'abc'
|
||||
ctx.readAndWriteToken = '42def'
|
||||
ctx.readAndWriteTokenPrefix = '42'
|
||||
ctx.TokenGenerator.promises.generateUniqueReadOnlyToken.resolves(
|
||||
ctx.readOnlyToken
|
||||
)
|
||||
this.TokenGenerator.readAndWriteToken.returns({
|
||||
token: this.readAndWriteToken,
|
||||
numericPrefix: this.readAndWriteTokenPrefix,
|
||||
ctx.TokenGenerator.readAndWriteToken.returns({
|
||||
token: ctx.readAndWriteToken,
|
||||
numericPrefix: ctx.readAndWriteTokenPrefix,
|
||||
})
|
||||
})
|
||||
|
||||
it('should get the project', async function () {
|
||||
await this.handler.promises.ensureTokensArePresent(this.project._id)
|
||||
expect(this.ProjectGetter.promises.getProject).to.have.been.calledOnce
|
||||
expect(this.ProjectGetter.promises.getProject).to.have.been.calledWith(
|
||||
this.project._id,
|
||||
it('should get the project', async function (ctx) {
|
||||
await ctx.handler.promises.ensureTokensArePresent(ctx.project._id)
|
||||
expect(ctx.ProjectGetter.promises.getProject).to.have.been.calledOnce
|
||||
expect(ctx.ProjectGetter.promises.getProject).to.have.been.calledWith(
|
||||
ctx.project._id,
|
||||
{
|
||||
tokens: 1,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should update the project with new tokens', async function () {
|
||||
await this.handler.promises.ensureTokensArePresent(this.project._id)
|
||||
expect(this.TokenGenerator.promises.generateUniqueReadOnlyToken).to.have
|
||||
it('should update the project with new tokens', async function (ctx) {
|
||||
await ctx.handler.promises.ensureTokensArePresent(ctx.project._id)
|
||||
expect(ctx.TokenGenerator.promises.generateUniqueReadOnlyToken).to.have
|
||||
.been.calledOnce
|
||||
expect(this.TokenGenerator.readAndWriteToken).to.have.been.calledOnce
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledOnce
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: this.project._id },
|
||||
expect(ctx.TokenGenerator.readAndWriteToken).to.have.been.calledOnce
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledOnce
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: ctx.project._id },
|
||||
{
|
||||
$set: {
|
||||
tokens: {
|
||||
readOnly: this.readOnlyToken,
|
||||
readAndWrite: this.readAndWriteToken,
|
||||
readAndWritePrefix: this.readAndWriteTokenPrefix,
|
||||
readOnly: ctx.readOnlyToken,
|
||||
readAndWrite: ctx.readAndWriteToken,
|
||||
readAndWritePrefix: ctx.readAndWriteTokenPrefix,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -637,10 +663,10 @@ describe('ProjectDetailsHandler', function () {
|
||||
})
|
||||
|
||||
describe('clearTokens', function () {
|
||||
it('clears the tokens from the project', async function () {
|
||||
await this.handler.promises.clearTokens(this.project._id)
|
||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: this.project._id },
|
||||
it('clears the tokens from the project', async function (ctx) {
|
||||
await ctx.handler.promises.clearTokens(ctx.project._id)
|
||||
expect(ctx.ProjectModel.updateOne).to.have.been.calledWith(
|
||||
{ _id: ctx.project._id },
|
||||
{ $unset: { tokens: 1 }, $set: { publicAccesLevel: 'private' } }
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
||||
const modulePath = '../../../../app/src/Features/Project/ProjectEntityHandler'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const Errors = require('../../../../app/src/Features/Errors/Errors')
|
||||
|
||||
vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
||||
vi.importActual('../../../../app/src/Features/Errors/Errors.js')
|
||||
)
|
||||
|
||||
describe('ProjectEntityHandler', function () {
|
||||
const projectId = '4eecb1c1bffa66588e0000a1'
|
||||
const docId = '4eecb1c1bffa66588e0000a2'
|
||||
|
||||
beforeEach(function () {
|
||||
this.TpdsUpdateSender = {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.TpdsUpdateSender = {
|
||||
addDoc: sinon.stub().callsArg(1),
|
||||
addFile: sinon.stub().callsArg(1),
|
||||
}
|
||||
this.ProjectModel = class Project {
|
||||
ctx.ProjectModel = class Project {
|
||||
constructor(options) {
|
||||
this._id = projectId
|
||||
this.name = 'project_name_here'
|
||||
@@ -21,59 +24,77 @@ describe('ProjectEntityHandler', function () {
|
||||
this.rootFolder = [this.rootFolder]
|
||||
}
|
||||
}
|
||||
this.project = new this.ProjectModel()
|
||||
ctx.project = new ctx.ProjectModel()
|
||||
|
||||
this.ProjectLocator = { findElement: sinon.stub() }
|
||||
this.DocumentUpdaterHandler = {
|
||||
ctx.ProjectLocator = { findElement: sinon.stub() }
|
||||
ctx.DocumentUpdaterHandler = {
|
||||
updateProjectStructure: sinon.stub().yields(),
|
||||
}
|
||||
this.callback = sinon.stub()
|
||||
ctx.callback = sinon.stub()
|
||||
|
||||
this.ProjectEntityHandler = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'../Docstore/DocstoreManager': (this.DocstoreManager = {
|
||||
promises: {},
|
||||
}),
|
||||
'../../Features/DocumentUpdater/DocumentUpdaterHandler':
|
||||
this.DocumentUpdaterHandler,
|
||||
'../../models/Project': {
|
||||
Project: this.ProjectModel,
|
||||
},
|
||||
'./ProjectLocator': this.ProjectLocator,
|
||||
'./ProjectGetter': (this.ProjectGetter = { promises: {} }),
|
||||
'../ThirdPartyDataStore/TpdsUpdateSender': this.TpdsUpdateSender,
|
||||
},
|
||||
})
|
||||
vi.doMock('../../../../app/src/Features/Docstore/DocstoreManager', () => ({
|
||||
default: (ctx.DocstoreManager = {
|
||||
promises: {},
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/DocumentUpdater/DocumentUpdaterHandler',
|
||||
() => ({
|
||||
default: ctx.DocumentUpdaterHandler,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/models/Project', () => ({
|
||||
Project: ctx.ProjectModel,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectLocator', () => ({
|
||||
default: ctx.ProjectLocator,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectGetter', () => ({
|
||||
default: (ctx.ProjectGetter = { promises: {} }),
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/ThirdPartyDataStore/TpdsUpdateSender',
|
||||
() => ({
|
||||
default: ctx.TpdsUpdateSender,
|
||||
})
|
||||
)
|
||||
|
||||
ctx.ProjectEntityHandler = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('getting folders, docs and files', function () {
|
||||
beforeEach(function () {
|
||||
this.project.rootFolder = [
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project.rootFolder = [
|
||||
{
|
||||
docs: [
|
||||
(this.doc1 = {
|
||||
(ctx.doc1 = {
|
||||
name: 'doc1',
|
||||
_id: 'doc1_id',
|
||||
}),
|
||||
],
|
||||
fileRefs: [
|
||||
(this.file1 = {
|
||||
(ctx.file1 = {
|
||||
rev: 1,
|
||||
_id: 'file1_id',
|
||||
name: 'file1',
|
||||
}),
|
||||
],
|
||||
folders: [
|
||||
(this.folder1 = {
|
||||
(ctx.folder1 = {
|
||||
name: 'folder1',
|
||||
docs: [
|
||||
(this.doc2 = {
|
||||
(ctx.doc2 = {
|
||||
name: 'doc2',
|
||||
_id: 'doc2_id',
|
||||
}),
|
||||
],
|
||||
fileRefs: [
|
||||
(this.file2 = {
|
||||
(ctx.file2 = {
|
||||
rev: 2,
|
||||
name: 'file2',
|
||||
_id: 'file2_id',
|
||||
@@ -84,54 +105,54 @@ describe('ProjectEntityHandler', function () {
|
||||
],
|
||||
},
|
||||
]
|
||||
this.ProjectGetter.promises.getProjectWithoutDocLines = sinon
|
||||
ctx.ProjectGetter.promises.getProjectWithoutDocLines = sinon
|
||||
.stub()
|
||||
.resolves(this.project)
|
||||
.resolves(ctx.project)
|
||||
})
|
||||
|
||||
describe('getAllDocs', function () {
|
||||
let fetchedDocs
|
||||
beforeEach(async function () {
|
||||
this.docs = [
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.docs = [
|
||||
{
|
||||
_id: this.doc1._id,
|
||||
lines: (this.lines1 = ['one']),
|
||||
rev: (this.rev1 = 1),
|
||||
_id: ctx.doc1._id,
|
||||
lines: (ctx.lines1 = ['one']),
|
||||
rev: (ctx.rev1 = 1),
|
||||
},
|
||||
{
|
||||
_id: this.doc2._id,
|
||||
lines: (this.lines2 = ['two']),
|
||||
rev: (this.rev2 = 2),
|
||||
_id: ctx.doc2._id,
|
||||
lines: (ctx.lines2 = ['two']),
|
||||
rev: (ctx.rev2 = 2),
|
||||
},
|
||||
]
|
||||
this.DocstoreManager.promises.getAllDocs = sinon
|
||||
ctx.DocstoreManager.promises.getAllDocs = sinon
|
||||
.stub()
|
||||
.resolves(this.docs)
|
||||
.resolves(ctx.docs)
|
||||
fetchedDocs =
|
||||
await this.ProjectEntityHandler.promises.getAllDocs(projectId)
|
||||
await ctx.ProjectEntityHandler.promises.getAllDocs(projectId)
|
||||
})
|
||||
|
||||
it('should get the doc lines and rev from the docstore', function () {
|
||||
this.DocstoreManager.promises.getAllDocs
|
||||
it('should get the doc lines and rev from the docstore', function (ctx) {
|
||||
ctx.DocstoreManager.promises.getAllDocs
|
||||
.calledWith(projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call the callback with the docs with the lines and rev included', function () {
|
||||
it('should call the callback with the docs with the lines and rev included', function (ctx) {
|
||||
expect(fetchedDocs).to.deep.equal({
|
||||
'/doc1': {
|
||||
_id: this.doc1._id,
|
||||
lines: this.lines1,
|
||||
name: this.doc1.name,
|
||||
rev: this.rev1,
|
||||
folder: this.project.rootFolder[0],
|
||||
_id: ctx.doc1._id,
|
||||
lines: ctx.lines1,
|
||||
name: ctx.doc1.name,
|
||||
rev: ctx.rev1,
|
||||
folder: ctx.project.rootFolder[0],
|
||||
},
|
||||
'/folder1/doc2': {
|
||||
_id: this.doc2._id,
|
||||
lines: this.lines2,
|
||||
name: this.doc2.name,
|
||||
rev: this.rev2,
|
||||
folder: this.folder1,
|
||||
_id: ctx.doc2._id,
|
||||
lines: ctx.lines2,
|
||||
name: ctx.doc2.name,
|
||||
rev: ctx.rev2,
|
||||
folder: ctx.folder1,
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -139,85 +160,85 @@ describe('ProjectEntityHandler', function () {
|
||||
|
||||
describe('getAllFiles', function () {
|
||||
let allFiles
|
||||
beforeEach(async function () {
|
||||
this.callback = sinon.stub()
|
||||
allFiles = await this.ProjectEntityHandler.promises.getAllFiles(
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.callback = sinon.stub()
|
||||
allFiles = await ctx.ProjectEntityHandler.promises.getAllFiles(
|
||||
projectId,
|
||||
this.callback
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should call the callback with the files', function () {
|
||||
it('should call the callback with the files', function (ctx) {
|
||||
expect(allFiles).to.deep.equal({
|
||||
'/file1': { ...this.file1, folder: this.project.rootFolder[0] },
|
||||
'/folder1/file2': { ...this.file2, folder: this.folder1 },
|
||||
'/file1': { ...ctx.file1, folder: ctx.project.rootFolder[0] },
|
||||
'/folder1/file2': { ...ctx.file2, folder: ctx.folder1 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAllDocPathsFromProject', function () {
|
||||
beforeEach(function () {
|
||||
this.docs = [
|
||||
beforeEach(function (ctx) {
|
||||
ctx.docs = [
|
||||
{
|
||||
_id: this.doc1._id,
|
||||
lines: (this.lines1 = ['one']),
|
||||
rev: (this.rev1 = 1),
|
||||
_id: ctx.doc1._id,
|
||||
lines: (ctx.lines1 = ['one']),
|
||||
rev: (ctx.rev1 = 1),
|
||||
},
|
||||
{
|
||||
_id: this.doc2._id,
|
||||
lines: (this.lines2 = ['two']),
|
||||
rev: (this.rev2 = 2),
|
||||
_id: ctx.doc2._id,
|
||||
lines: (ctx.lines2 = ['two']),
|
||||
rev: (ctx.rev2 = 2),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
it('should call the callback with the path for each docId', function () {
|
||||
it('should call the callback with the path for each docId', function (ctx) {
|
||||
const expected = {
|
||||
[this.doc1._id]: `/${this.doc1.name}`,
|
||||
[this.doc2._id]: `/folder1/${this.doc2.name}`,
|
||||
[ctx.doc1._id]: `/${ctx.doc1.name}`,
|
||||
[ctx.doc2._id]: `/folder1/${ctx.doc2.name}`,
|
||||
}
|
||||
expect(
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProject(
|
||||
this.project,
|
||||
this.callback
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProject(
|
||||
ctx.project,
|
||||
ctx.callback
|
||||
)
|
||||
).to.deep.equal(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDocPathByProjectIdAndDocId', function () {
|
||||
it('should call the callback with the path for an existing doc id at the root level', async function () {
|
||||
it('should call the callback with the path for an existing doc id at the root level', async function (ctx) {
|
||||
const path =
|
||||
await this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
await ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
this.doc1._id
|
||||
ctx.doc1._id
|
||||
)
|
||||
expect(path).to.deep.equal(`/${this.doc1.name}`)
|
||||
expect(path).to.deep.equal(`/${ctx.doc1.name}`)
|
||||
})
|
||||
|
||||
it('should call the callback with the path for an existing doc id nested within a folder', async function () {
|
||||
it('should call the callback with the path for an existing doc id nested within a folder', async function (ctx) {
|
||||
const path =
|
||||
await this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
await ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
this.doc2._id
|
||||
ctx.doc2._id
|
||||
)
|
||||
expect(path).to.deep.equal(`/folder1/${this.doc2.name}`)
|
||||
expect(path).to.deep.equal(`/folder1/${ctx.doc2.name}`)
|
||||
})
|
||||
|
||||
it('should call the callback with a NotFoundError for a non-existing doc', async function () {
|
||||
it('should call the callback with a NotFoundError for a non-existing doc', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
'non-existing-id'
|
||||
)
|
||||
).to.be.rejectedWith(Errors.NotFoundError)
|
||||
})
|
||||
|
||||
it('should call the callback with a NotFoundError for an existing file', async function () {
|
||||
it('should call the callback with a NotFoundError for an existing file', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
this.file1._id
|
||||
ctx.file1._id
|
||||
)
|
||||
).to.be.rejectedWith(Errors.NotFoundError)
|
||||
})
|
||||
@@ -225,66 +246,66 @@ describe('ProjectEntityHandler', function () {
|
||||
|
||||
describe('_getAllFolders', async function () {
|
||||
let folders
|
||||
beforeEach(async function () {
|
||||
this.callback = sinon.stub()
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.callback = sinon.stub()
|
||||
folders =
|
||||
await this.ProjectEntityHandler.promises._getAllFolders(projectId)
|
||||
await ctx.ProjectEntityHandler.promises._getAllFolders(projectId)
|
||||
})
|
||||
|
||||
it('should get the project without the docs lines', function () {
|
||||
this.ProjectGetter.promises.getProjectWithoutDocLines
|
||||
it('should get the project without the docs lines', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProjectWithoutDocLines
|
||||
.calledWith(projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call the callback with the folders', function () {
|
||||
it('should call the callback with the folders', function (ctx) {
|
||||
expect(folders).to.deep.equal([
|
||||
{ path: '/', folder: this.project.rootFolder[0] },
|
||||
{ path: '/folder1', folder: this.folder1 },
|
||||
{ path: '/', folder: ctx.project.rootFolder[0] },
|
||||
{ path: '/folder1', folder: ctx.folder1 },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('_getAllFoldersFromProject', function () {
|
||||
it('should return the folders', function () {
|
||||
it('should return the folders', function (ctx) {
|
||||
expect(
|
||||
this.ProjectEntityHandler._getAllFoldersFromProject(this.project)
|
||||
ctx.ProjectEntityHandler._getAllFoldersFromProject(ctx.project)
|
||||
).to.deep.equal([
|
||||
{ path: '/', folder: this.project.rootFolder[0] },
|
||||
{ path: '/folder1', folder: this.folder1 },
|
||||
{ path: '/', folder: ctx.project.rootFolder[0] },
|
||||
{ path: '/folder1', folder: ctx.folder1 },
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an invalid file tree', function () {
|
||||
beforeEach(function () {
|
||||
this.project.rootFolder = [
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project.rootFolder = [
|
||||
{
|
||||
docs: [
|
||||
(this.doc1 = {
|
||||
(ctx.doc1 = {
|
||||
name: null, // invalid doc name
|
||||
_id: 'doc1_id',
|
||||
}),
|
||||
],
|
||||
fileRefs: [
|
||||
(this.file1 = {
|
||||
(ctx.file1 = {
|
||||
rev: 1,
|
||||
_id: 'file1_id',
|
||||
name: null, // invalid file name
|
||||
}),
|
||||
],
|
||||
folders: [
|
||||
(this.folder1 = {
|
||||
(ctx.folder1 = {
|
||||
name: null, // invalid folder name
|
||||
docs: [
|
||||
(this.doc2 = {
|
||||
(ctx.doc2 = {
|
||||
name: 'doc2',
|
||||
_id: 'doc2_id',
|
||||
}),
|
||||
],
|
||||
fileRefs: [
|
||||
(this.file2 = {
|
||||
(ctx.file2 = {
|
||||
rev: 2,
|
||||
name: 'file2',
|
||||
_id: 'file2_id',
|
||||
@@ -296,107 +317,107 @@ describe('ProjectEntityHandler', function () {
|
||||
],
|
||||
},
|
||||
]
|
||||
this.ProjectGetter.promises.getProjectWithoutDocLines = sinon
|
||||
ctx.ProjectGetter.promises.getProjectWithoutDocLines = sinon
|
||||
.stub()
|
||||
.resolves(this.project)
|
||||
.resolves(ctx.project)
|
||||
})
|
||||
|
||||
describe('getAllDocs', function () {
|
||||
beforeEach(async function () {
|
||||
this.docs = [
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.docs = [
|
||||
{
|
||||
_id: this.doc1._id,
|
||||
lines: (this.lines1 = ['one']),
|
||||
rev: (this.rev1 = 1),
|
||||
_id: ctx.doc1._id,
|
||||
lines: (ctx.lines1 = ['one']),
|
||||
rev: (ctx.rev1 = 1),
|
||||
},
|
||||
{
|
||||
_id: this.doc2._id,
|
||||
lines: (this.lines2 = ['two']),
|
||||
rev: (this.rev2 = 2),
|
||||
_id: ctx.doc2._id,
|
||||
lines: (ctx.lines2 = ['two']),
|
||||
rev: (ctx.rev2 = 2),
|
||||
},
|
||||
]
|
||||
this.DocstoreManager.promises.getAllDocs = sinon
|
||||
ctx.DocstoreManager.promises.getAllDocs = sinon
|
||||
.stub()
|
||||
.resolves(this.docs)
|
||||
.resolves(ctx.docs)
|
||||
})
|
||||
|
||||
it('should call the callback with an error', async function () {
|
||||
await expect(this.ProjectEntityHandler.promises.getAllDocs(projectId))
|
||||
.to.be.rejected
|
||||
it('should call the callback with an error', async function (ctx) {
|
||||
await expect(ctx.ProjectEntityHandler.promises.getAllDocs(projectId)).to
|
||||
.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAllFiles', function () {
|
||||
it('should call the callback with and error', async function () {
|
||||
await expect(this.ProjectEntityHandler.promises.getAllFiles(projectId))
|
||||
it('should call the callback with and error', async function (ctx) {
|
||||
await expect(ctx.ProjectEntityHandler.promises.getAllFiles(projectId))
|
||||
.to.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDocPathByProjectIdAndDocId', function () {
|
||||
it('should call the callback with an error for an existing doc id at the root level', async function () {
|
||||
it('should call the callback with an error for an existing doc id at the root level', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
this.doc1._id
|
||||
ctx.doc1._id
|
||||
)
|
||||
).to.be.rejectedWith(Error)
|
||||
})
|
||||
|
||||
it('should call the callback with an error for an existing doc id nested within a folder', async function () {
|
||||
it('should call the callback with an error for an existing doc id nested within a folder', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
this.doc2._id
|
||||
ctx.doc2._id
|
||||
)
|
||||
).to.be.rejectedWith(Error)
|
||||
})
|
||||
|
||||
it('should call the callback with an error for a non-existing doc', async function () {
|
||||
it('should call the callback with an error for a non-existing doc', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
'non-existing-id'
|
||||
)
|
||||
).to.be.rejectedWith(Error)
|
||||
})
|
||||
|
||||
it('should call the callback with an error for an existing file', async function () {
|
||||
it('should call the callback with an error for an existing file', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathByProjectIdAndDocId(
|
||||
projectId,
|
||||
this.file1._id
|
||||
ctx.file1._id
|
||||
)
|
||||
).to.be.rejectedWith(Error)
|
||||
})
|
||||
})
|
||||
|
||||
describe('_getAllFolders', function () {
|
||||
it('should call the callback with an error', async function () {
|
||||
it('should call the callback with an error', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises._getAllFolders(projectId)
|
||||
ctx.ProjectEntityHandler.promises._getAllFolders(projectId)
|
||||
).to.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAllEntities', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter.promises.getProject = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject = sinon
|
||||
.stub()
|
||||
.resolves(this.project)
|
||||
.resolves(ctx.project)
|
||||
})
|
||||
|
||||
it('should call the callback with an error', async function () {
|
||||
it('should call the callback with an error', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getAllEntities(projectId)
|
||||
ctx.ProjectEntityHandler.promises.getAllEntities(projectId)
|
||||
).to.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAllDocPathsFromProjectById', function () {
|
||||
it('should call the callback with an error', async function () {
|
||||
it('should call the callback with an error', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getAllDocPathsFromProjectById(
|
||||
ctx.ProjectEntityHandler.promises.getAllDocPathsFromProjectById(
|
||||
projectId
|
||||
)
|
||||
).to.be.rejected
|
||||
@@ -404,11 +425,11 @@ describe('ProjectEntityHandler', function () {
|
||||
})
|
||||
|
||||
describe('getDocPathFromProjectByDocId', function () {
|
||||
it('should call the callback with an error', async function () {
|
||||
it('should call the callback with an error', async function (ctx) {
|
||||
await expect(
|
||||
this.ProjectEntityHandler.promises.getDocPathFromProjectByDocId(
|
||||
ctx.ProjectEntityHandler.promises.getDocPathFromProjectByDocId(
|
||||
projectId,
|
||||
this.doc1._id
|
||||
ctx.doc1._id
|
||||
)
|
||||
).to.be.rejected
|
||||
})
|
||||
@@ -416,26 +437,26 @@ describe('ProjectEntityHandler', function () {
|
||||
})
|
||||
|
||||
describe('getDoc', function () {
|
||||
beforeEach(function () {
|
||||
this.lines = ['mock', 'doc', 'lines']
|
||||
this.rev = 5
|
||||
this.version = 42
|
||||
this.ranges = { mock: 'ranges' }
|
||||
this.callback = sinon.stub()
|
||||
this.DocstoreManager.promises.getDoc = sinon.stub().resolves({
|
||||
lines: this.lines,
|
||||
rev: this.rev,
|
||||
version: this.version,
|
||||
ranges: this.ranges,
|
||||
beforeEach(function (ctx) {
|
||||
ctx.lines = ['mock', 'doc', 'lines']
|
||||
ctx.rev = 5
|
||||
ctx.version = 42
|
||||
ctx.ranges = { mock: 'ranges' }
|
||||
ctx.callback = sinon.stub()
|
||||
ctx.DocstoreManager.promises.getDoc = sinon.stub().resolves({
|
||||
lines: ctx.lines,
|
||||
rev: ctx.rev,
|
||||
version: ctx.version,
|
||||
ranges: ctx.ranges,
|
||||
})
|
||||
})
|
||||
|
||||
it('should call the callback with the lines, version and rev', async function () {
|
||||
const doc = await this.ProjectEntityHandler.promises.getDoc(
|
||||
it('should call the callback with the lines, version and rev', async function (ctx) {
|
||||
const doc = await ctx.ProjectEntityHandler.promises.getDoc(
|
||||
projectId,
|
||||
docId
|
||||
)
|
||||
this.DocstoreManager.promises.getDoc
|
||||
ctx.DocstoreManager.promises.getDoc
|
||||
.calledWith(projectId, docId)
|
||||
.should.equal(true)
|
||||
expect(doc).to.exist
|
||||
@@ -445,32 +466,32 @@ describe('ProjectEntityHandler', function () {
|
||||
describe('promises.getDoc', function () {
|
||||
let result
|
||||
|
||||
beforeEach(async function () {
|
||||
this.lines = ['mock', 'doc', 'lines']
|
||||
this.rev = 5
|
||||
this.version = 42
|
||||
this.ranges = { mock: 'ranges' }
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.lines = ['mock', 'doc', 'lines']
|
||||
ctx.rev = 5
|
||||
ctx.version = 42
|
||||
ctx.ranges = { mock: 'ranges' }
|
||||
|
||||
this.DocstoreManager.promises.getDoc = sinon.stub().resolves({
|
||||
lines: this.lines,
|
||||
rev: this.rev,
|
||||
version: this.version,
|
||||
ranges: this.ranges,
|
||||
ctx.DocstoreManager.promises.getDoc = sinon.stub().resolves({
|
||||
lines: ctx.lines,
|
||||
rev: ctx.rev,
|
||||
version: ctx.version,
|
||||
ranges: ctx.ranges,
|
||||
})
|
||||
result = await this.ProjectEntityHandler.promises.getDoc(projectId, docId)
|
||||
result = await ctx.ProjectEntityHandler.promises.getDoc(projectId, docId)
|
||||
})
|
||||
|
||||
it('should call the docstore', function () {
|
||||
this.DocstoreManager.promises.getDoc
|
||||
it('should call the docstore', function (ctx) {
|
||||
ctx.DocstoreManager.promises.getDoc
|
||||
.calledWith(projectId, docId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the lines, rev, version and ranges', function () {
|
||||
expect(result.lines).to.equal(this.lines)
|
||||
expect(result.rev).to.equal(this.rev)
|
||||
expect(result.version).to.equal(this.version)
|
||||
expect(result.ranges).to.equal(this.ranges)
|
||||
it('should return the lines, rev, version and ranges', function (ctx) {
|
||||
expect(result.lines).to.equal(ctx.lines)
|
||||
expect(result.rev).to.equal(ctx.rev)
|
||||
expect(result.version).to.equal(ctx.version)
|
||||
expect(result.ranges).to.equal(ctx.ranges)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,30 +1,31 @@
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
const modulePath = '../../../../app/src/Features/Project/ProjectGetter.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const { ObjectId } = require('mongodb-legacy')
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
const modulePath = '../../../../app/src/Features/Project/ProjectGetter.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
describe('ProjectGetter', function () {
|
||||
beforeEach(function () {
|
||||
this.project = { _id: new ObjectId() }
|
||||
this.projectIdStr = this.project._id.toString()
|
||||
this.deletedProject = { deleterData: { wombat: 'potato' } }
|
||||
this.userId = new ObjectId()
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.project = { _id: new ObjectId() }
|
||||
ctx.projectIdStr = ctx.project._id.toString()
|
||||
ctx.deletedProject = { deleterData: { wombat: 'potato' } }
|
||||
ctx.userId = new ObjectId()
|
||||
|
||||
this.DeletedProject = {
|
||||
ctx.DeletedProject = {
|
||||
find: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves([this.deletedProject]),
|
||||
exec: sinon.stub().resolves([ctx.deletedProject]),
|
||||
}),
|
||||
}
|
||||
this.Project = {
|
||||
ctx.Project = {
|
||||
find: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(),
|
||||
}),
|
||||
findOne: sinon.stub().returns({
|
||||
exec: sinon.stub().resolves(this.project),
|
||||
exec: sinon.stub().resolves(ctx.project),
|
||||
}),
|
||||
}
|
||||
this.CollaboratorsGetter = {
|
||||
ctx.CollaboratorsGetter = {
|
||||
promises: {
|
||||
getProjectsUserIsMemberOf: sinon.stub().resolves({
|
||||
readAndWrite: [],
|
||||
@@ -34,58 +35,76 @@ describe('ProjectGetter', function () {
|
||||
}),
|
||||
},
|
||||
}
|
||||
this.LockManager = {
|
||||
ctx.LockManager = {
|
||||
promises: {
|
||||
runWithLock: sinon
|
||||
.stub()
|
||||
.callsFake((namespace, id, runner) => runner()),
|
||||
},
|
||||
}
|
||||
this.db = {
|
||||
ctx.db = {
|
||||
projects: {
|
||||
findOne: sinon.stub().resolves(this.project),
|
||||
findOne: sinon.stub().resolves(ctx.project),
|
||||
},
|
||||
users: {},
|
||||
}
|
||||
this.ProjectEntityMongoUpdateHandler = {
|
||||
ctx.ProjectEntityMongoUpdateHandler = {
|
||||
lockKey: sinon.stub().returnsArg(0),
|
||||
}
|
||||
this.ProjectGetter = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'../../infrastructure/mongodb': { db: this.db, ObjectId },
|
||||
'../../models/Project': {
|
||||
Project: this.Project,
|
||||
},
|
||||
'../../models/DeletedProject': {
|
||||
DeletedProject: this.DeletedProject,
|
||||
},
|
||||
'../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
|
||||
'../../infrastructure/LockManager': this.LockManager,
|
||||
'./ProjectEntityMongoUpdateHandler':
|
||||
this.ProjectEntityMongoUpdateHandler,
|
||||
},
|
||||
})
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/mongodb', () => ({
|
||||
db: ctx.db,
|
||||
ObjectId,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/Project', () => ({
|
||||
Project: ctx.Project,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/DeletedProject', () => ({
|
||||
DeletedProject: ctx.DeletedProject,
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Collaborators/CollaboratorsGetter',
|
||||
() => ({
|
||||
default: ctx.CollaboratorsGetter,
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/LockManager', () => ({
|
||||
default: ctx.LockManager,
|
||||
}))
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Project/ProjectEntityMongoUpdateHandler',
|
||||
() => ({
|
||||
default: ctx.ProjectEntityMongoUpdateHandler,
|
||||
})
|
||||
)
|
||||
|
||||
ctx.ProjectGetter = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('getProjectWithoutDocLines', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter.promises.getProject = sinon.stub().resolves()
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject = sinon.stub().resolves()
|
||||
})
|
||||
|
||||
describe('passing an id', function () {
|
||||
beforeEach(async function () {
|
||||
await this.ProjectGetter.promises.getProjectWithoutDocLines(
|
||||
this.project._id
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getProjectWithoutDocLines(
|
||||
ctx.project._id
|
||||
)
|
||||
})
|
||||
|
||||
it('should call find with the project id', function () {
|
||||
this.ProjectGetter.promises.getProject
|
||||
.calledWith(this.project._id)
|
||||
it('should call find with the project id', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject
|
||||
.calledWith(ctx.project._id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should exclude the doc lines', function () {
|
||||
it('should exclude the doc lines', function (ctx) {
|
||||
const excludes = {
|
||||
'rootFolder.docs.lines': 0,
|
||||
'rootFolder.folders.docs.lines': 0,
|
||||
@@ -97,32 +116,32 @@ describe('ProjectGetter', function () {
|
||||
'rootFolder.folders.folders.folders.folders.folders.folders.folders.docs.lines': 0,
|
||||
}
|
||||
|
||||
this.ProjectGetter.promises.getProject
|
||||
.calledWith(this.project._id, excludes)
|
||||
ctx.ProjectGetter.promises.getProject
|
||||
.calledWith(ctx.project._id, excludes)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getProjectWithOnlyFolders', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter.promises.getProject = sinon.stub().resolves()
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject = sinon.stub().resolves()
|
||||
})
|
||||
|
||||
describe('passing an id', function () {
|
||||
beforeEach(async function () {
|
||||
await this.ProjectGetter.promises.getProjectWithOnlyFolders(
|
||||
this.project._id
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getProjectWithOnlyFolders(
|
||||
ctx.project._id
|
||||
)
|
||||
})
|
||||
|
||||
it('should call find with the project id', function () {
|
||||
this.ProjectGetter.promises.getProject
|
||||
.calledWith(this.project._id)
|
||||
it('should call find with the project id', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject
|
||||
.calledWith(ctx.project._id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should exclude the docs and files lines', function () {
|
||||
it('should exclude the docs and files lines', function (ctx) {
|
||||
const excludes = {
|
||||
'rootFolder.docs': 0,
|
||||
'rootFolder.fileRefs': 0,
|
||||
@@ -141,8 +160,8 @@ describe('ProjectGetter', function () {
|
||||
'rootFolder.folders.folders.folders.folders.folders.folders.folders.docs': 0,
|
||||
'rootFolder.folders.folders.folders.folders.folders.folders.folders.fileRefs': 0,
|
||||
}
|
||||
this.ProjectGetter.promises.getProject
|
||||
.calledWith(this.project._id, excludes)
|
||||
ctx.ProjectGetter.promises.getProject
|
||||
.calledWith(ctx.project._id, excludes)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
@@ -151,58 +170,58 @@ describe('ProjectGetter', function () {
|
||||
describe('getProject', function () {
|
||||
describe('without projection', function () {
|
||||
describe('with project id', function () {
|
||||
beforeEach(async function () {
|
||||
await this.ProjectGetter.promises.getProject(this.projectIdStr)
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getProject(ctx.projectIdStr)
|
||||
})
|
||||
|
||||
it('should call findOne with the project id', function () {
|
||||
expect(this.db.projects.findOne.callCount).to.equal(1)
|
||||
it('should call findOne with the project id', function (ctx) {
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(1)
|
||||
expect(
|
||||
this.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(this.projectIdStr)
|
||||
ctx.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(ctx.projectIdStr)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without project id', function () {
|
||||
it('should be rejected', function () {
|
||||
it('should be rejected', function (ctx) {
|
||||
expect(
|
||||
this.ProjectGetter.promises.getProject(null)
|
||||
ctx.ProjectGetter.promises.getProject(null)
|
||||
).to.be.rejectedWith('no project id provided')
|
||||
expect(this.db.projects.findOne.callCount).to.equal(0)
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with projection', function () {
|
||||
beforeEach(function () {
|
||||
this.projection = { _id: 1 }
|
||||
beforeEach(function (ctx) {
|
||||
ctx.projection = { _id: 1 }
|
||||
})
|
||||
|
||||
describe('with project id', function () {
|
||||
beforeEach(async function () {
|
||||
await this.ProjectGetter.promises.getProject(
|
||||
this.projectIdStr,
|
||||
this.projection
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getProject(
|
||||
ctx.projectIdStr,
|
||||
ctx.projection
|
||||
)
|
||||
})
|
||||
|
||||
it('should call findOne with the project id', function () {
|
||||
expect(this.db.projects.findOne.callCount).to.equal(1)
|
||||
it('should call findOne with the project id', function (ctx) {
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(1)
|
||||
expect(
|
||||
this.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(this.projectIdStr)
|
||||
expect(this.db.projects.findOne.lastCall.args[1]).to.deep.equal({
|
||||
projection: this.projection,
|
||||
ctx.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(ctx.projectIdStr)
|
||||
expect(ctx.db.projects.findOne.lastCall.args[1]).to.deep.equal({
|
||||
projection: ctx.projection,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('without project id', function () {
|
||||
it('should be rejected', function () {
|
||||
it('should be rejected', function (ctx) {
|
||||
expect(
|
||||
this.ProjectGetter.promises.getProject(null)
|
||||
ctx.ProjectGetter.promises.getProject(null)
|
||||
).to.be.rejectedWith('no project id provided')
|
||||
expect(this.db.projects.findOne.callCount).to.equal(0)
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -211,155 +230,151 @@ describe('ProjectGetter', function () {
|
||||
describe('getProjectWithoutLock', function () {
|
||||
describe('without projection', function () {
|
||||
describe('with project id', function () {
|
||||
beforeEach(async function () {
|
||||
await this.ProjectGetter.promises.getProjectWithoutLock(
|
||||
this.projectIdStr
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getProjectWithoutLock(
|
||||
ctx.projectIdStr
|
||||
)
|
||||
})
|
||||
|
||||
it('should call findOne with the project id', function () {
|
||||
expect(this.db.projects.findOne.callCount).to.equal(1)
|
||||
it('should call findOne with the project id', function (ctx) {
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(1)
|
||||
expect(
|
||||
this.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(this.projectIdStr)
|
||||
ctx.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(ctx.projectIdStr)
|
||||
})
|
||||
})
|
||||
|
||||
describe('without project id', function () {
|
||||
it('should be rejected', function () {
|
||||
it('should be rejected', function (ctx) {
|
||||
expect(
|
||||
this.ProjectGetter.promises.getProjectWithoutLock(null)
|
||||
ctx.ProjectGetter.promises.getProjectWithoutLock(null)
|
||||
).to.be.rejectedWith('no project id provided')
|
||||
expect(this.db.projects.findOne.callCount).to.equal(0)
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with projection', function () {
|
||||
beforeEach(function () {
|
||||
this.projection = { _id: 1 }
|
||||
beforeEach(function (ctx) {
|
||||
ctx.projection = { _id: 1 }
|
||||
})
|
||||
|
||||
describe('with project id', function () {
|
||||
beforeEach(async function () {
|
||||
await this.ProjectGetter.promises.getProjectWithoutLock(
|
||||
this.project._id,
|
||||
this.projection
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getProjectWithoutLock(
|
||||
ctx.project._id,
|
||||
ctx.projection
|
||||
)
|
||||
})
|
||||
|
||||
it('should call findOne with the project id', function () {
|
||||
expect(this.db.projects.findOne.callCount).to.equal(1)
|
||||
it('should call findOne with the project id', function (ctx) {
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(1)
|
||||
expect(
|
||||
this.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(this.projectIdStr)
|
||||
expect(this.db.projects.findOne.lastCall.args[1]).to.deep.equal({
|
||||
projection: this.projection,
|
||||
ctx.db.projects.findOne.lastCall.args[0]._id.toString()
|
||||
).to.equal(ctx.projectIdStr)
|
||||
expect(ctx.db.projects.findOne.lastCall.args[1]).to.deep.equal({
|
||||
projection: ctx.projection,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('without project id', function () {
|
||||
it('should be rejected', function () {
|
||||
it('should be rejected', function (ctx) {
|
||||
expect(
|
||||
this.ProjectGetter.promises.getProjectWithoutLock(null)
|
||||
ctx.ProjectGetter.promises.getProjectWithoutLock(null)
|
||||
).to.be.rejectedWith('no project id provided')
|
||||
expect(this.db.projects.findOne.callCount).to.equal(0)
|
||||
expect(ctx.db.projects.findOne.callCount).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('findAllUsersProjects', function () {
|
||||
beforeEach(function () {
|
||||
this.fields = { mock: 'fields' }
|
||||
this.projectOwned = { _id: 'mock-owned-projects' }
|
||||
this.projectRW = { _id: 'mock-rw-projects' }
|
||||
this.projectReview = { _id: 'mock-review-projects' }
|
||||
this.projectRO = { _id: 'mock-ro-projects' }
|
||||
this.projectTokenRW = { _id: 'mock-token-rw-projects' }
|
||||
this.projectTokenRO = { _id: 'mock-token-ro-projects' }
|
||||
this.Project.find
|
||||
.withArgs({ owner_ref: this.userId }, this.fields)
|
||||
.returns({ exec: sinon.stub().resolves([this.projectOwned]) })
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fields = { mock: 'fields' }
|
||||
ctx.projectOwned = { _id: 'mock-owned-projects' }
|
||||
ctx.projectRW = { _id: 'mock-rw-projects' }
|
||||
ctx.projectReview = { _id: 'mock-review-projects' }
|
||||
ctx.projectRO = { _id: 'mock-ro-projects' }
|
||||
ctx.projectTokenRW = { _id: 'mock-token-rw-projects' }
|
||||
ctx.projectTokenRO = { _id: 'mock-token-ro-projects' }
|
||||
ctx.Project.find
|
||||
.withArgs({ owner_ref: ctx.userId }, ctx.fields)
|
||||
.returns({ exec: sinon.stub().resolves([ctx.projectOwned]) })
|
||||
})
|
||||
|
||||
it('should return a promise with all the projects', async function () {
|
||||
this.CollaboratorsGetter.promises.getProjectsUserIsMemberOf.resolves({
|
||||
readAndWrite: [this.projectRW],
|
||||
readOnly: [this.projectRO],
|
||||
tokenReadAndWrite: [this.projectTokenRW],
|
||||
tokenReadOnly: [this.projectTokenRO],
|
||||
review: [this.projectReview],
|
||||
it('should return a promise with all the projects', async function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.getProjectsUserIsMemberOf.resolves({
|
||||
readAndWrite: [ctx.projectRW],
|
||||
readOnly: [ctx.projectRO],
|
||||
tokenReadAndWrite: [ctx.projectTokenRW],
|
||||
tokenReadOnly: [ctx.projectTokenRO],
|
||||
review: [ctx.projectReview],
|
||||
})
|
||||
const projects = await this.ProjectGetter.promises.findAllUsersProjects(
|
||||
this.userId,
|
||||
this.fields
|
||||
const projects = await ctx.ProjectGetter.promises.findAllUsersProjects(
|
||||
ctx.userId,
|
||||
ctx.fields
|
||||
)
|
||||
|
||||
expect(projects).to.deep.equal({
|
||||
owned: [this.projectOwned],
|
||||
readAndWrite: [this.projectRW],
|
||||
readOnly: [this.projectRO],
|
||||
tokenReadAndWrite: [this.projectTokenRW],
|
||||
tokenReadOnly: [this.projectTokenRO],
|
||||
review: [this.projectReview],
|
||||
owned: [ctx.projectOwned],
|
||||
readAndWrite: [ctx.projectRW],
|
||||
readOnly: [ctx.projectRO],
|
||||
tokenReadAndWrite: [ctx.projectTokenRW],
|
||||
tokenReadOnly: [ctx.projectTokenRO],
|
||||
review: [ctx.projectReview],
|
||||
})
|
||||
})
|
||||
|
||||
it('should remove duplicate projects', async function () {
|
||||
this.CollaboratorsGetter.promises.getProjectsUserIsMemberOf.resolves({
|
||||
readAndWrite: [this.projectRW, this.projectOwned],
|
||||
readOnly: [this.projectRO, this.projectRW],
|
||||
tokenReadAndWrite: [this.projectTokenRW, this.projectRO],
|
||||
tokenReadOnly: [
|
||||
this.projectTokenRW,
|
||||
this.projectTokenRO,
|
||||
this.projectRO,
|
||||
],
|
||||
review: [this.projectReview],
|
||||
it('should remove duplicate projects', async function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.getProjectsUserIsMemberOf.resolves({
|
||||
readAndWrite: [ctx.projectRW, ctx.projectOwned],
|
||||
readOnly: [ctx.projectRO, ctx.projectRW],
|
||||
tokenReadAndWrite: [ctx.projectTokenRW, ctx.projectRO],
|
||||
tokenReadOnly: [ctx.projectTokenRW, ctx.projectTokenRO, ctx.projectRO],
|
||||
review: [ctx.projectReview],
|
||||
})
|
||||
const projects = await this.ProjectGetter.promises.findAllUsersProjects(
|
||||
this.userId,
|
||||
this.fields
|
||||
const projects = await ctx.ProjectGetter.promises.findAllUsersProjects(
|
||||
ctx.userId,
|
||||
ctx.fields
|
||||
)
|
||||
|
||||
expect(projects).to.deep.equal({
|
||||
owned: [this.projectOwned],
|
||||
readAndWrite: [this.projectRW],
|
||||
readOnly: [this.projectRO],
|
||||
tokenReadAndWrite: [this.projectTokenRW],
|
||||
tokenReadOnly: [this.projectTokenRO],
|
||||
review: [this.projectReview],
|
||||
owned: [ctx.projectOwned],
|
||||
readAndWrite: [ctx.projectRW],
|
||||
readOnly: [ctx.projectRO],
|
||||
tokenReadAndWrite: [ctx.projectTokenRW],
|
||||
tokenReadOnly: [ctx.projectTokenRO],
|
||||
review: [ctx.projectReview],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getProjectIdByReadAndWriteToken', function () {
|
||||
describe('when project find returns project', function () {
|
||||
this.beforeEach(async function () {
|
||||
this.projectIdFound =
|
||||
await this.ProjectGetter.promises.getProjectIdByReadAndWriteToken(
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.projectIdFound =
|
||||
await ctx.ProjectGetter.promises.getProjectIdByReadAndWriteToken(
|
||||
'token'
|
||||
)
|
||||
})
|
||||
|
||||
it('should find project with token', function () {
|
||||
this.Project.findOne
|
||||
it('should find project with token', function (ctx) {
|
||||
ctx.Project.findOne
|
||||
.calledWithMatch({ 'tokens.readAndWrite': 'token' })
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the project id', function () {
|
||||
expect(this.projectIdFound).to.equal(this.project._id)
|
||||
it('should return the project id', function (ctx) {
|
||||
expect(ctx.projectIdFound).to.equal(ctx.project._id)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when project not found', function () {
|
||||
it('should return undefined', async function () {
|
||||
this.Project.findOne.returns({ exec: sinon.stub().resolves(null) })
|
||||
it('should return undefined', async function (ctx) {
|
||||
ctx.Project.findOne.returns({ exec: sinon.stub().resolves(null) })
|
||||
const projectId =
|
||||
await this.ProjectGetter.promises.getProjectIdByReadAndWriteToken(
|
||||
await ctx.ProjectGetter.promises.getProjectIdByReadAndWriteToken(
|
||||
'token'
|
||||
)
|
||||
|
||||
@@ -368,86 +383,79 @@ describe('ProjectGetter', function () {
|
||||
})
|
||||
|
||||
describe('when project find returns error', function () {
|
||||
this.beforeEach(async function () {
|
||||
this.Project.findOne.returns({ exec: sinon.stub().rejects() })
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.Project.findOne.returns({ exec: sinon.stub().rejects() })
|
||||
})
|
||||
|
||||
it('should be rejected', function () {
|
||||
it('should be rejected', function (ctx) {
|
||||
expect(
|
||||
this.ProjectGetter.promises.getProjectIdByReadAndWriteToken('token')
|
||||
ctx.ProjectGetter.promises.getProjectIdByReadAndWriteToken('token')
|
||||
).to.be.rejected
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('findUsersProjectsByName', function () {
|
||||
it('should perform a case-insensitive search', async function () {
|
||||
this.project1 = { _id: 1, name: 'find me!' }
|
||||
this.project2 = { _id: 2, name: 'not me!' }
|
||||
this.project3 = { _id: 3, name: 'FIND ME!' }
|
||||
this.project4 = { _id: 4, name: 'Find Me!' }
|
||||
this.Project.find.withArgs({ owner_ref: this.userId }).returns({
|
||||
it('should perform a case-insensitive search', async function (ctx) {
|
||||
ctx.project1 = { _id: 1, name: 'find me!' }
|
||||
ctx.project2 = { _id: 2, name: 'not me!' }
|
||||
ctx.project3 = { _id: 3, name: 'FIND ME!' }
|
||||
ctx.project4 = { _id: 4, name: 'Find Me!' }
|
||||
ctx.Project.find.withArgs({ owner_ref: ctx.userId }).returns({
|
||||
exec: sinon
|
||||
.stub()
|
||||
.resolves([
|
||||
this.project1,
|
||||
this.project2,
|
||||
this.project3,
|
||||
this.project4,
|
||||
]),
|
||||
.resolves([ctx.project1, ctx.project2, ctx.project3, ctx.project4]),
|
||||
})
|
||||
const projects =
|
||||
await this.ProjectGetter.promises.findUsersProjectsByName(
|
||||
this.userId,
|
||||
this.project1.name
|
||||
)
|
||||
const projects = await ctx.ProjectGetter.promises.findUsersProjectsByName(
|
||||
ctx.userId,
|
||||
ctx.project1.name
|
||||
)
|
||||
const projectNames = projects.map(project => project.name)
|
||||
expect(projectNames).to.have.members([
|
||||
this.project1.name,
|
||||
this.project3.name,
|
||||
this.project4.name,
|
||||
ctx.project1.name,
|
||||
ctx.project3.name,
|
||||
ctx.project4.name,
|
||||
])
|
||||
})
|
||||
|
||||
it('should search collaborations as well', async function () {
|
||||
this.project1 = { _id: 1, name: 'find me!' }
|
||||
this.project2 = { _id: 2, name: 'FIND ME!' }
|
||||
this.project3 = { _id: 3, name: 'Find Me!' }
|
||||
this.project4 = { _id: 4, name: 'find ME!' }
|
||||
this.project5 = { _id: 5, name: 'FIND me!' }
|
||||
this.Project.find
|
||||
.withArgs({ owner_ref: this.userId })
|
||||
.returns({ exec: sinon.stub().resolves([this.project1]) })
|
||||
this.CollaboratorsGetter.promises.getProjectsUserIsMemberOf.resolves({
|
||||
readAndWrite: [this.project2],
|
||||
readOnly: [this.project3],
|
||||
tokenReadAndWrite: [this.project4],
|
||||
tokenReadOnly: [this.project5],
|
||||
it('should search collaborations as well', async function (ctx) {
|
||||
ctx.project1 = { _id: 1, name: 'find me!' }
|
||||
ctx.project2 = { _id: 2, name: 'FIND ME!' }
|
||||
ctx.project3 = { _id: 3, name: 'Find Me!' }
|
||||
ctx.project4 = { _id: 4, name: 'find ME!' }
|
||||
ctx.project5 = { _id: 5, name: 'FIND me!' }
|
||||
ctx.Project.find
|
||||
.withArgs({ owner_ref: ctx.userId })
|
||||
.returns({ exec: sinon.stub().resolves([ctx.project1]) })
|
||||
ctx.CollaboratorsGetter.promises.getProjectsUserIsMemberOf.resolves({
|
||||
readAndWrite: [ctx.project2],
|
||||
readOnly: [ctx.project3],
|
||||
tokenReadAndWrite: [ctx.project4],
|
||||
tokenReadOnly: [ctx.project5],
|
||||
})
|
||||
const projects =
|
||||
await this.ProjectGetter.promises.findUsersProjectsByName(
|
||||
this.userId,
|
||||
this.project1.name
|
||||
)
|
||||
const projects = await ctx.ProjectGetter.promises.findUsersProjectsByName(
|
||||
ctx.userId,
|
||||
ctx.project1.name
|
||||
)
|
||||
expect(projects.map(project => project.name)).to.have.members([
|
||||
this.project1.name,
|
||||
this.project2.name,
|
||||
ctx.project1.name,
|
||||
ctx.project2.name,
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUsersDeletedProjects', function () {
|
||||
it('should look up the deleted projects by deletedProjectOwnerId', async function () {
|
||||
await this.ProjectGetter.promises.getUsersDeletedProjects('giraffe')
|
||||
sinon.assert.calledWith(this.DeletedProject.find, {
|
||||
it('should look up the deleted projects by deletedProjectOwnerId', async function (ctx) {
|
||||
await ctx.ProjectGetter.promises.getUsersDeletedProjects('giraffe')
|
||||
sinon.assert.calledWith(ctx.DeletedProject.find, {
|
||||
'deleterData.deletedProjectOwnerId': 'giraffe',
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass the found projects to the callback', async function () {
|
||||
it('should pass the found projects to the callback', async function (ctx) {
|
||||
const docs =
|
||||
await this.ProjectGetter.promises.getUsersDeletedProjects('giraffe')
|
||||
expect(docs).to.deep.equal([this.deletedProject])
|
||||
await ctx.ProjectGetter.promises.getUsersDeletedProjects('giraffe')
|
||||
expect(docs).to.deep.equal([ctx.deletedProject])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -46,11 +46,14 @@ describe('ProjectHistoryHandler', function () {
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/Features/History/HistoryManager.mjs', () => ({
|
||||
default: (ctx.HistoryManager = {
|
||||
promises: {},
|
||||
}),
|
||||
}))
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/History/HistoryManager.mjs',
|
||||
() => ({
|
||||
default: (ctx.HistoryManager = {
|
||||
promises: {},
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Project/ProjectEntityUpdateHandler',
|
||||
|
||||
@@ -5,10 +5,13 @@ import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
||||
|
||||
const ObjectId = mongodb.ObjectId
|
||||
|
||||
const MODULE_PATH = new URL(
|
||||
'../../../../app/src/Features/Project/ProjectListController',
|
||||
import.meta.url
|
||||
).pathname
|
||||
const MODULE_PATH = `${import.meta.dirname}/../../../../app/src/Features/Project/ProjectListController`
|
||||
|
||||
// Mock AnalyticsManager as it isn't used in these tests but causes the User model to be imported
|
||||
// TODO: remove this once all models are ESM and this kind of mocking is no longer necessary
|
||||
vi.mock('../../../../app/src/Features/Analytics/AnalyticsManager.js', () => {
|
||||
return {}
|
||||
})
|
||||
|
||||
describe('ProjectListController', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
const { expect } = require('chai')
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
||||
const modulePath = '../../../../app/src/Features/Project/ProjectLocator'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
const Errors = require('../../../../app/src/Features/Errors/Errors')
|
||||
|
||||
vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
||||
vi.importActual('../../../../app/src/Features/Errors/Errors.js')
|
||||
)
|
||||
|
||||
const project = { _id: '1234566', rootFolder: [] }
|
||||
const rootDoc = { name: 'rootDoc', _id: 'das239djd' }
|
||||
@@ -38,47 +41,50 @@ project.rootFolder[0] = rootFolder
|
||||
project.rootDoc_id = rootDoc._id
|
||||
|
||||
describe('ProjectLocator', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter = {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ProjectGetter = {
|
||||
getProject: sinon.stub().callsArgWith(2, null, project),
|
||||
}
|
||||
this.ProjectHelper = {
|
||||
ctx.ProjectHelper = {
|
||||
isArchived: sinon.stub(),
|
||||
isTrashed: sinon.stub(),
|
||||
isArchivedOrTrashed: sinon.stub(),
|
||||
}
|
||||
this.locator = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'../../models/User': { User: this.User },
|
||||
'./ProjectGetter': this.ProjectGetter,
|
||||
'./ProjectHelper': this.ProjectHelper,
|
||||
},
|
||||
})
|
||||
|
||||
vi.doMock('../../../../app/src/models/User', () => ({
|
||||
default: { User: ctx.User },
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectGetter', () => ({
|
||||
default: ctx.ProjectGetter,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectHelper', () => ({
|
||||
default: ctx.ProjectHelper,
|
||||
}))
|
||||
|
||||
ctx.locator = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('finding a doc', function () {
|
||||
it('finds one at the root level', async function () {
|
||||
const { element, path, folder } = await this.locator.promises.findElement(
|
||||
{
|
||||
project_id: project._id,
|
||||
element_id: doc2._id,
|
||||
type: 'docs',
|
||||
}
|
||||
)
|
||||
it('finds one at the root level', async function (ctx) {
|
||||
const { element, path, folder } = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: doc2._id,
|
||||
type: 'docs',
|
||||
})
|
||||
element._id.should.equal(doc2._id)
|
||||
path.fileSystem.should.equal(`/${doc2.name}`)
|
||||
folder._id.should.equal(project.rootFolder[0]._id)
|
||||
path.mongo.should.equal('rootFolder.0.docs.1')
|
||||
})
|
||||
|
||||
it('when it is nested', async function () {
|
||||
const { element, path, folder } = await this.locator.promises.findElement(
|
||||
{
|
||||
project_id: project._id,
|
||||
element_id: subSubDoc._id,
|
||||
type: 'doc',
|
||||
}
|
||||
)
|
||||
it('when it is nested', async function (ctx) {
|
||||
const { element, path, folder } = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: subSubDoc._id,
|
||||
type: 'doc',
|
||||
})
|
||||
expect(element._id).to.equal(subSubDoc._id)
|
||||
path.fileSystem.should.equal(
|
||||
`/${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}`
|
||||
@@ -87,9 +93,9 @@ describe('ProjectLocator', function () {
|
||||
path.mongo.should.equal('rootFolder.0.folders.1.folders.0.docs.0')
|
||||
})
|
||||
|
||||
it('should give error if element could not be found', async function () {
|
||||
it('should give error if element could not be found', async function (ctx) {
|
||||
await expect(
|
||||
this.locator.promises.findElement({
|
||||
ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: 'ddsd432nj42',
|
||||
type: 'docs',
|
||||
@@ -101,20 +107,18 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('finding a folder', function () {
|
||||
it('should return root folder when looking for root folder', async function () {
|
||||
const { element: foundElement } = await this.locator.promises.findElement(
|
||||
{
|
||||
project_id: project._id,
|
||||
element_id: rootFolder._id,
|
||||
type: 'folder',
|
||||
}
|
||||
)
|
||||
it('should return root folder when looking for root folder', async function (ctx) {
|
||||
const { element: foundElement } = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: rootFolder._id,
|
||||
type: 'folder',
|
||||
})
|
||||
foundElement._id.should.equal(rootFolder._id)
|
||||
})
|
||||
|
||||
it('should not return root folder when searching for docs', async function () {
|
||||
it('should not return root folder when searching for docs', async function (ctx) {
|
||||
await expect(
|
||||
this.locator.promises.findElement({
|
||||
ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: rootFolder._id,
|
||||
type: 'docs',
|
||||
@@ -124,9 +128,9 @@ describe('ProjectLocator', function () {
|
||||
.and.eventually.have.property('message', 'entity not found')
|
||||
})
|
||||
|
||||
it('should not return root folder when searching for files', async function () {
|
||||
it('should not return root folder when searching for files', async function (ctx) {
|
||||
await expect(
|
||||
this.locator.promises.findElement({
|
||||
ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: rootFolder._id,
|
||||
type: 'files',
|
||||
@@ -136,12 +140,12 @@ describe('ProjectLocator', function () {
|
||||
.and.eventually.have.property('message', 'entity not found')
|
||||
})
|
||||
|
||||
it('when at root', async function () {
|
||||
it('when at root', async function (ctx) {
|
||||
const {
|
||||
element: foundElement,
|
||||
path,
|
||||
folder: parentFolder,
|
||||
} = await this.locator.promises.findElement({
|
||||
} = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: subFolder._id,
|
||||
type: 'folder',
|
||||
@@ -152,12 +156,12 @@ describe('ProjectLocator', function () {
|
||||
path.mongo.should.equal('rootFolder.0.folders.1')
|
||||
})
|
||||
|
||||
it('when deeply nested', async function () {
|
||||
it('when deeply nested', async function (ctx) {
|
||||
const {
|
||||
element: foundElement,
|
||||
path,
|
||||
folder: parentFolder,
|
||||
} = await this.locator.promises.findElement({
|
||||
} = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: secondSubFolder._id,
|
||||
type: 'folder',
|
||||
@@ -171,12 +175,12 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('finding a file', function () {
|
||||
it('when at root', async function () {
|
||||
it('when at root', async function (ctx) {
|
||||
const {
|
||||
element: foundElement,
|
||||
path,
|
||||
folder: parentFolder,
|
||||
} = await this.locator.promises.findElement({
|
||||
} = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: file1._id,
|
||||
type: 'fileRefs',
|
||||
@@ -187,12 +191,12 @@ describe('ProjectLocator', function () {
|
||||
path.mongo.should.equal('rootFolder.0.fileRefs.0')
|
||||
})
|
||||
|
||||
it('when deeply nested', async function () {
|
||||
it('when deeply nested', async function (ctx) {
|
||||
const {
|
||||
element: foundElement,
|
||||
path,
|
||||
folder: parentFolder,
|
||||
} = await this.locator.promises.findElement({
|
||||
} = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: subSubFile._id,
|
||||
type: 'fileRefs',
|
||||
@@ -207,25 +211,21 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('finding an element with wrong element type', function () {
|
||||
it('should add an s onto the element type', async function () {
|
||||
const { element: foundElement } = await this.locator.promises.findElement(
|
||||
{
|
||||
project_id: project._id,
|
||||
element_id: subSubDoc._id,
|
||||
type: 'doc',
|
||||
}
|
||||
)
|
||||
it('should add an s onto the element type', async function (ctx) {
|
||||
const { element: foundElement } = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: subSubDoc._id,
|
||||
type: 'doc',
|
||||
})
|
||||
foundElement._id.should.equal(subSubDoc._id)
|
||||
})
|
||||
|
||||
it('should convert file to fileRefs', async function () {
|
||||
const { element: foundElement } = await this.locator.promises.findElement(
|
||||
{
|
||||
project_id: project._id,
|
||||
element_id: file1._id,
|
||||
type: 'fileRefs',
|
||||
}
|
||||
)
|
||||
it('should convert file to fileRefs', async function (ctx) {
|
||||
const { element: foundElement } = await ctx.locator.promises.findElement({
|
||||
project_id: project._id,
|
||||
element_id: file1._id,
|
||||
type: 'fileRefs',
|
||||
})
|
||||
foundElement._id.should.equal(file1._id)
|
||||
})
|
||||
})
|
||||
@@ -243,12 +243,12 @@ describe('ProjectLocator', function () {
|
||||
_id: '1234566',
|
||||
rootFolder: [rootFolder2],
|
||||
}
|
||||
it('should find doc in project', async function () {
|
||||
it('should find doc in project', async function (ctx) {
|
||||
const {
|
||||
element: foundElement,
|
||||
path,
|
||||
folder: parentFolder,
|
||||
} = await this.locator.promises.findElement({
|
||||
} = await ctx.locator.promises.findElement({
|
||||
project: project2,
|
||||
element_id: doc3._id,
|
||||
type: 'docs',
|
||||
@@ -261,38 +261,38 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('finding root doc', function () {
|
||||
it('should return root doc when passed project', async function () {
|
||||
const { element: doc } = await this.locator.promises.findRootDoc(project)
|
||||
it('should return root doc when passed project', async function (ctx) {
|
||||
const { element: doc } = await ctx.locator.promises.findRootDoc(project)
|
||||
doc._id.should.equal(rootDoc._id)
|
||||
})
|
||||
|
||||
it('should return root doc when passed project_id', async function () {
|
||||
const { element: doc } = await this.locator.promises.findRootDoc(
|
||||
it('should return root doc when passed project_id', async function (ctx) {
|
||||
const { element: doc } = await ctx.locator.promises.findRootDoc(
|
||||
project._id
|
||||
)
|
||||
doc._id.should.equal(rootDoc._id)
|
||||
})
|
||||
|
||||
it('should return null when the project has no rootDoc', async function () {
|
||||
it('should return null when the project has no rootDoc', async function (ctx) {
|
||||
project.rootDoc_id = null
|
||||
const { element: rootDoc } =
|
||||
await this.locator.promises.findRootDoc(project)
|
||||
await ctx.locator.promises.findRootDoc(project)
|
||||
expect(rootDoc).to.equal(null)
|
||||
})
|
||||
|
||||
it('should return null when the rootDoc_id no longer exists', async function () {
|
||||
it('should return null when the rootDoc_id no longer exists', async function (ctx) {
|
||||
project.rootDoc_id = 'doesntexist'
|
||||
const { element: rootDoc } =
|
||||
await this.locator.promises.findRootDoc(project)
|
||||
await ctx.locator.promises.findRootDoc(project)
|
||||
expect(rootDoc).to.equal(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('findElementByPath', function () {
|
||||
it('should take a doc path and return the element for a root level document', async function () {
|
||||
it('should take a doc path and return the element for a root level document', async function (ctx) {
|
||||
const path = `${doc1.name}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -301,10 +301,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(rootFolder)
|
||||
})
|
||||
|
||||
it('should take a doc path and return the element for a root level document with a starting slash', async function () {
|
||||
it('should take a doc path and return the element for a root level document with a starting slash', async function (ctx) {
|
||||
const path = `/${doc1.name}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -313,10 +313,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(rootFolder)
|
||||
})
|
||||
|
||||
it('should take a doc path and return the element for a nested document', async function () {
|
||||
it('should take a doc path and return the element for a nested document', async function (ctx) {
|
||||
const path = `${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -325,10 +325,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(secondSubFolder)
|
||||
})
|
||||
|
||||
it('should take a file path and return the element for a root level document', async function () {
|
||||
it('should take a file path and return the element for a root level document', async function (ctx) {
|
||||
const path = `${file1.name}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -337,10 +337,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(rootFolder)
|
||||
})
|
||||
|
||||
it('should take a file path and return the element for a nested document', async function () {
|
||||
it('should take a file path and return the element for a nested document', async function (ctx) {
|
||||
const path = `${subFolder.name}/${secondSubFolder.name}/${subSubFile.name}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -349,10 +349,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(secondSubFolder)
|
||||
})
|
||||
|
||||
it('should take a file path and return the element for a nested document case insenstive', async function () {
|
||||
it('should take a file path and return the element for a nested document case insenstive', async function (ctx) {
|
||||
const path = `${subFolder.name.toUpperCase()}/${secondSubFolder.name.toUpperCase()}/${subSubFile.name.toUpperCase()}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -361,10 +361,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(secondSubFolder)
|
||||
})
|
||||
|
||||
it('should not return elements with a case-insensitive match when exactCaseMatch is true', async function () {
|
||||
it('should not return elements with a case-insensitive match when exactCaseMatch is true', async function (ctx) {
|
||||
const path = `${subFolder.name.toUpperCase()}/${secondSubFolder.name.toUpperCase()}/${subSubFile.name.toUpperCase()}`
|
||||
await expect(
|
||||
this.locator.promises.findElementByPath({
|
||||
ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
exactCaseMatch: true,
|
||||
@@ -372,10 +372,10 @@ describe('ProjectLocator', function () {
|
||||
).to.eventually.be.rejected
|
||||
})
|
||||
|
||||
it('should take a file path and return the element for a nested folder', async function () {
|
||||
it('should take a file path and return the element for a nested folder', async function (ctx) {
|
||||
const path = `${subFolder.name}/${secondSubFolder.name}`
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -384,10 +384,10 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(subFolder)
|
||||
})
|
||||
|
||||
it('should take a file path and return the root folder', async function () {
|
||||
it('should take a file path and return the root folder', async function (ctx) {
|
||||
const path = '/'
|
||||
const { element, type, folder } =
|
||||
await this.locator.promises.findElementByPath({
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -396,16 +396,16 @@ describe('ProjectLocator', function () {
|
||||
expect(folder).to.equal(null)
|
||||
})
|
||||
|
||||
it('should return an error if the file can not be found inside know folder', async function () {
|
||||
it('should return an error if the file can not be found inside know folder', async function (ctx) {
|
||||
const path = `${subFolder.name}/${secondSubFolder.name}/exist.txt`
|
||||
await expect(this.locator.promises.findElementByPath({ project, path }))
|
||||
.to.eventually.be.rejected
|
||||
await expect(ctx.locator.promises.findElementByPath({ project, path })).to
|
||||
.eventually.be.rejected
|
||||
})
|
||||
|
||||
it('should return an error if the file can not be found inside unknown folder', async function () {
|
||||
it('should return an error if the file can not be found inside unknown folder', async function (ctx) {
|
||||
const path = 'this/does/not/exist.txt'
|
||||
await expect(
|
||||
this.locator.promises.findElementByPath({
|
||||
ctx.locator.promises.findElementByPath({
|
||||
project,
|
||||
path,
|
||||
})
|
||||
@@ -413,8 +413,8 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('where duplicate folder exists', function () {
|
||||
beforeEach(function () {
|
||||
this.duplicateFolder = {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.duplicateFolder = {
|
||||
name: 'duplicate1',
|
||||
_id: '1234',
|
||||
folders: [
|
||||
@@ -425,13 +425,13 @@ describe('ProjectLocator', function () {
|
||||
fileRefs: [],
|
||||
},
|
||||
],
|
||||
docs: [(this.doc = { name: 'main.tex', _id: '456' })],
|
||||
docs: [(ctx.doc = { name: 'main.tex', _id: '456' })],
|
||||
fileRefs: [],
|
||||
}
|
||||
this.project = {
|
||||
ctx.project = {
|
||||
rootFolder: [
|
||||
{
|
||||
folders: [this.duplicateFolder, this.duplicateFolder],
|
||||
folders: [ctx.duplicateFolder, ctx.duplicateFolder],
|
||||
fileRefs: [],
|
||||
docs: [],
|
||||
},
|
||||
@@ -439,26 +439,26 @@ describe('ProjectLocator', function () {
|
||||
}
|
||||
})
|
||||
|
||||
it('should not call the callback more than once', async function () {
|
||||
const path = `${this.duplicateFolder.name}/${this.doc.name}`
|
||||
await this.locator.promises.findElementByPath({
|
||||
project: this.project,
|
||||
it('should not call the callback more than once', async function (ctx) {
|
||||
const path = `${ctx.duplicateFolder.name}/${ctx.doc.name}`
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project: ctx.project,
|
||||
path,
|
||||
})
|
||||
}) // mocha will throw exception if done called multiple times
|
||||
|
||||
it('should not call the callback more than once when the path is longer than 1 level below the duplicate level', async function () {
|
||||
const path = `${this.duplicateFolder.name}/1/main.tex`
|
||||
await this.locator.promises.findElementByPath({
|
||||
project: this.project,
|
||||
it('should not call the callback more than once when the path is longer than 1 level below the duplicate level', async function (ctx) {
|
||||
const path = `${ctx.duplicateFolder.name}/1/main.tex`
|
||||
await ctx.locator.promises.findElementByPath({
|
||||
project: ctx.project,
|
||||
path,
|
||||
})
|
||||
})
|
||||
}) // mocha will throw exception if done called multiple times
|
||||
|
||||
describe('with a null doc', function () {
|
||||
beforeEach(function () {
|
||||
this.project = {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project = {
|
||||
rootFolder: [
|
||||
{
|
||||
folders: [],
|
||||
@@ -469,10 +469,10 @@ describe('ProjectLocator', function () {
|
||||
}
|
||||
})
|
||||
|
||||
it('should not crash with a null', async function () {
|
||||
it('should not crash with a null', async function (ctx) {
|
||||
const path = '/other.tex'
|
||||
const { element } = await this.locator.promises.findElementByPath({
|
||||
project: this.project,
|
||||
const { element } = await ctx.locator.promises.findElementByPath({
|
||||
project: ctx.project,
|
||||
path,
|
||||
})
|
||||
element.name.should.equal('other.tex')
|
||||
@@ -480,14 +480,14 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('with a null project', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter = { getProject: sinon.stub().callsArg(2) }
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectGetter = { getProject: sinon.stub().callsArg(2) }
|
||||
})
|
||||
|
||||
it('should not crash with a null', async function () {
|
||||
it('should not crash with a null', async function (ctx) {
|
||||
const path = '/other.tex'
|
||||
await expect(
|
||||
this.locator.promises.findElementByPath({
|
||||
ctx.locator.promises.findElementByPath({
|
||||
project_id: project._id,
|
||||
path,
|
||||
})
|
||||
@@ -496,15 +496,13 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('with a project_id', function () {
|
||||
it('should take a doc path and return the element for a root level document', async function () {
|
||||
it('should take a doc path and return the element for a root level document', async function (ctx) {
|
||||
const path = `${doc1.name}`
|
||||
const { element, type } = await this.locator.promises.findElementByPath(
|
||||
{
|
||||
project_id: project._id,
|
||||
path,
|
||||
}
|
||||
)
|
||||
this.ProjectGetter.getProject
|
||||
const { element, type } = await ctx.locator.promises.findElementByPath({
|
||||
project_id: project._id,
|
||||
path,
|
||||
})
|
||||
ctx.ProjectGetter.getProject
|
||||
.calledWith(project._id, { rootFolder: true, rootDoc_id: true })
|
||||
.should.equal(true)
|
||||
element.should.deep.equal(doc1)
|
||||
@@ -514,17 +512,17 @@ describe('ProjectLocator', function () {
|
||||
})
|
||||
|
||||
describe('findElementByMongoPath', function () {
|
||||
it('traverses the file tree like Mongo would do', function () {
|
||||
const element = this.locator.findElementByMongoPath(
|
||||
it('traverses the file tree like Mongo would do', function (ctx) {
|
||||
const element = ctx.locator.findElementByMongoPath(
|
||||
project,
|
||||
'rootFolder.0.folders.1.folders.0.fileRefs.0'
|
||||
)
|
||||
expect(element).to.equal(subSubFile)
|
||||
})
|
||||
|
||||
it('throws an error if no element is found', function () {
|
||||
it('throws an error if no element is found', function (ctx) {
|
||||
expect(() =>
|
||||
this.locator.findElementByMongoPath(
|
||||
ctx.locator.findElementByMongoPath(
|
||||
project,
|
||||
'rootolder.0.folders.0.folders.0.fileRefs.0'
|
||||
)
|
||||
|
||||
@@ -1,60 +1,81 @@
|
||||
const { expect } = require('chai')
|
||||
const { ObjectId } = require('mongodb-legacy')
|
||||
const sinon = require('sinon')
|
||||
import { vi, expect } from 'vitest'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import sinon from 'sinon'
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/Project/ProjectRootDocManager.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
'../../../../app/src/Features/Project/ProjectRootDocManager.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
describe('ProjectRootDocManager', function () {
|
||||
beforeEach(function () {
|
||||
this.project_id = 'project-123'
|
||||
this.docPaths = {}
|
||||
this.docId1 = new ObjectId()
|
||||
this.docId2 = new ObjectId()
|
||||
this.docId3 = new ObjectId()
|
||||
this.docId4 = new ObjectId()
|
||||
this.docPaths[this.docId1] = '/chapter1.tex'
|
||||
this.docPaths[this.docId2] = '/main.tex'
|
||||
this.docPaths[this.docId3] = '/nested/chapter1a.tex'
|
||||
this.docPaths[this.docId4] = '/nested/chapter1b.tex'
|
||||
this.sl_req_id = 'sl-req-id-123'
|
||||
this.callback = sinon.stub()
|
||||
this.globbyFiles = ['a.tex', 'b.tex', 'main.tex']
|
||||
this.globby = sinon.stub().resolves(this.globbyFiles)
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.project_id = 'project-123'
|
||||
ctx.docPaths = {}
|
||||
ctx.docId1 = new ObjectId()
|
||||
ctx.docId2 = new ObjectId()
|
||||
ctx.docId3 = new ObjectId()
|
||||
ctx.docId4 = new ObjectId()
|
||||
ctx.docPaths[ctx.docId1] = '/chapter1.tex'
|
||||
ctx.docPaths[ctx.docId2] = '/main.tex'
|
||||
ctx.docPaths[ctx.docId3] = '/nested/chapter1a.tex'
|
||||
ctx.docPaths[ctx.docId4] = '/nested/chapter1b.tex'
|
||||
ctx.sl_req_id = 'sl-req-id-123'
|
||||
ctx.callback = sinon.stub()
|
||||
ctx.globbyFiles = ['a.tex', 'b.tex', 'main.tex']
|
||||
ctx.globby = sinon.stub().resolves(ctx.globbyFiles)
|
||||
|
||||
this.fs = {
|
||||
ctx.fs = {
|
||||
readFile: sinon.stub().callsArgWith(2, new Error('file not found')),
|
||||
stat: sinon.stub().callsArgWith(1, null, { size: 100 }),
|
||||
}
|
||||
this.ProjectRootDocManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'./ProjectEntityHandler': (this.ProjectEntityHandler = {}),
|
||||
'./ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
|
||||
'./ProjectGetter': (this.ProjectGetter = {}),
|
||||
'../../infrastructure/GracefulShutdown': {
|
||||
BackgroundTaskTracker: class {
|
||||
add() {}
|
||||
done() {}
|
||||
},
|
||||
},
|
||||
globby: this.globby,
|
||||
fs: this.fs,
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Project/ProjectEntityHandler',
|
||||
() => ({
|
||||
default: (ctx.ProjectEntityHandler = {}),
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/Features/Project/ProjectEntityUpdateHandler',
|
||||
() => ({
|
||||
default: (ctx.ProjectEntityUpdateHandler = {}),
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/Features/Project/ProjectGetter', () => ({
|
||||
default: (ctx.ProjectGetter = {}),
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/GracefulShutdown', () => ({
|
||||
BackgroundTaskTracker: class {
|
||||
add() {}
|
||||
done() {}
|
||||
},
|
||||
})
|
||||
}))
|
||||
|
||||
vi.doMock('globby', () => ({
|
||||
default: ctx.globby,
|
||||
}))
|
||||
|
||||
vi.doMock('fs', () => ({
|
||||
default: ctx.fs,
|
||||
}))
|
||||
|
||||
ctx.ProjectRootDocManager = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('setRootDocAutomatically', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
this.ProjectEntityUpdateHandler.isPathValidForRootDoc = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
ctx.ProjectEntityUpdateHandler.isPathValidForRootDoc = sinon
|
||||
.stub()
|
||||
.returns(true)
|
||||
})
|
||||
describe('when there is a suitable root doc', function () {
|
||||
beforeEach(async function () {
|
||||
this.docs = {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.docs = {
|
||||
'/chapter1.tex': {
|
||||
_id: this.docId1,
|
||||
_id: ctx.docId1,
|
||||
lines: [
|
||||
'something else',
|
||||
'\\begin{document}',
|
||||
@@ -63,7 +84,7 @@ describe('ProjectRootDocManager', function () {
|
||||
],
|
||||
},
|
||||
'/main.tex': {
|
||||
_id: this.docId2,
|
||||
_id: ctx.docId2,
|
||||
lines: [
|
||||
'different line',
|
||||
'\\documentclass{article}',
|
||||
@@ -71,114 +92,114 @@ describe('ProjectRootDocManager', function () {
|
||||
],
|
||||
},
|
||||
'/nested/chapter1a.tex': {
|
||||
_id: this.docId3,
|
||||
_id: ctx.docId3,
|
||||
lines: ['Hello world'],
|
||||
},
|
||||
'/nested/chapter1b.tex': {
|
||||
_id: this.docId4,
|
||||
_id: ctx.docId4,
|
||||
lines: ['Hello world'],
|
||||
},
|
||||
}
|
||||
this.ProjectEntityHandler.getAllDocs = sinon
|
||||
ctx.ProjectEntityHandler.getAllDocs = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docs)
|
||||
await this.ProjectRootDocManager.promises.setRootDocAutomatically(
|
||||
this.project_id
|
||||
.callsArgWith(1, null, ctx.docs)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocAutomatically(
|
||||
ctx.project_id
|
||||
)
|
||||
})
|
||||
|
||||
it('should check the docs of the project', function () {
|
||||
this.ProjectEntityHandler.getAllDocs
|
||||
.calledWith(this.project_id)
|
||||
it('should check the docs of the project', function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocs
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should set the root doc to the doc containing a documentclass', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(this.project_id, this.docId2)
|
||||
it('should set the root doc to the doc containing a documentclass', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(ctx.project_id, ctx.docId2)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the root doc is an Rtex file', function () {
|
||||
beforeEach(async function () {
|
||||
this.docs = {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.docs = {
|
||||
'/chapter1.tex': {
|
||||
_id: this.docId1,
|
||||
_id: ctx.docId1,
|
||||
lines: ['\\begin{document}', 'Hello world', '\\end{document}'],
|
||||
},
|
||||
'/main.Rtex': {
|
||||
_id: this.docId2,
|
||||
_id: ctx.docId2,
|
||||
lines: ['\\documentclass{article}', '\\input{chapter1}'],
|
||||
},
|
||||
}
|
||||
this.ProjectEntityHandler.getAllDocs = sinon
|
||||
ctx.ProjectEntityHandler.getAllDocs = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docs)
|
||||
await this.ProjectRootDocManager.promises.setRootDocAutomatically(
|
||||
this.project_id
|
||||
.callsArgWith(1, null, ctx.docs)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocAutomatically(
|
||||
ctx.project_id
|
||||
)
|
||||
})
|
||||
|
||||
it('should set the root doc to the doc containing a documentclass', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(this.project_id, this.docId2)
|
||||
it('should set the root doc to the doc containing a documentclass', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(ctx.project_id, ctx.docId2)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is no suitable root doc', function () {
|
||||
beforeEach(async function () {
|
||||
this.docs = {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.docs = {
|
||||
'/chapter1.tex': {
|
||||
_id: this.docId1,
|
||||
_id: ctx.docId1,
|
||||
lines: ['\\begin{document}', 'Hello world', '\\end{document}'],
|
||||
},
|
||||
'/style.bst': {
|
||||
_id: this.docId2,
|
||||
_id: ctx.docId2,
|
||||
lines: ['%Example: \\documentclass{article}'],
|
||||
},
|
||||
}
|
||||
this.ProjectEntityHandler.getAllDocs = sinon
|
||||
ctx.ProjectEntityHandler.getAllDocs = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docs)
|
||||
await this.ProjectRootDocManager.promises.setRootDocAutomatically(
|
||||
this.project_id
|
||||
.callsArgWith(1, null, ctx.docs)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocAutomatically(
|
||||
ctx.project_id
|
||||
)
|
||||
})
|
||||
|
||||
it('should not set the root doc to the doc containing a documentclass', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(false)
|
||||
it('should not set the root doc to the doc containing a documentclass', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('findRootDocFileFromDirectory', function () {
|
||||
beforeEach(function () {
|
||||
this.fs.readFile
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/a.tex')
|
||||
.callsArgWith(2, null, 'Hello World!')
|
||||
this.fs.readFile
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/b.tex')
|
||||
.callsArgWith(2, null, "I'm a little teapot, get me out of here.")
|
||||
this.fs.readFile
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/main.tex')
|
||||
.callsArgWith(2, null, "Help, I'm trapped in a unit testing factory")
|
||||
this.fs.readFile
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/c.tex')
|
||||
.callsArgWith(2, null, 'Tomato, tomahto.')
|
||||
this.fs.readFile
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/a/a.tex')
|
||||
.callsArgWith(2, null, 'Potato? Potahto. Potootee!')
|
||||
this.documentclassContent = '% test\n\\documentclass\n% test'
|
||||
ctx.documentclassContent = '% test\n\\documentclass\n% test'
|
||||
})
|
||||
|
||||
describe('when there is a file in a subfolder', function () {
|
||||
beforeEach(function () {
|
||||
beforeEach(function (ctx) {
|
||||
// have to splice globbyFiles weirdly because of the way the stubbed globby method handles references
|
||||
this.globbyFiles.splice(
|
||||
ctx.globbyFiles.splice(
|
||||
0,
|
||||
this.globbyFiles.length,
|
||||
ctx.globbyFiles.length,
|
||||
'c.tex',
|
||||
'a.tex',
|
||||
'a/a.tex',
|
||||
@@ -186,90 +207,90 @@ describe('ProjectRootDocManager', function () {
|
||||
)
|
||||
})
|
||||
|
||||
it('processes the root folder files first, and then the subfolder, in alphabetical order', async function () {
|
||||
it('processes the root folder files first, and then the subfolder, in alphabetical order', async function (ctx) {
|
||||
const { path } =
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(path).to.equal('a.tex')
|
||||
sinon.assert.callOrder(
|
||||
this.fs.readFile.withArgs('/foo/a.tex'),
|
||||
this.fs.readFile.withArgs('/foo/b.tex'),
|
||||
this.fs.readFile.withArgs('/foo/c.tex'),
|
||||
this.fs.readFile.withArgs('/foo/a/a.tex')
|
||||
ctx.fs.readFile.withArgs('/foo/a.tex'),
|
||||
ctx.fs.readFile.withArgs('/foo/b.tex'),
|
||||
ctx.fs.readFile.withArgs('/foo/c.tex'),
|
||||
ctx.fs.readFile.withArgs('/foo/a/a.tex')
|
||||
)
|
||||
})
|
||||
|
||||
it('processes smaller files first', async function () {
|
||||
this.fs.stat.withArgs('/foo/c.tex').callsArgWith(1, null, { size: 1 })
|
||||
it('processes smaller files first', async function (ctx) {
|
||||
ctx.fs.stat.withArgs('/foo/c.tex').callsArgWith(1, null, { size: 1 })
|
||||
const { path } =
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(path).to.equal('c.tex')
|
||||
sinon.assert.callOrder(
|
||||
this.fs.readFile.withArgs('/foo/c.tex'),
|
||||
this.fs.readFile.withArgs('/foo/a.tex'),
|
||||
this.fs.readFile.withArgs('/foo/b.tex'),
|
||||
this.fs.readFile.withArgs('/foo/a/a.tex')
|
||||
ctx.fs.readFile.withArgs('/foo/c.tex'),
|
||||
ctx.fs.readFile.withArgs('/foo/a.tex'),
|
||||
ctx.fs.readFile.withArgs('/foo/b.tex'),
|
||||
ctx.fs.readFile.withArgs('/foo/a/a.tex')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when main.tex contains a documentclass', function () {
|
||||
beforeEach(function () {
|
||||
this.fs.readFile
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/main.tex')
|
||||
.callsArgWith(2, null, this.documentclassContent)
|
||||
.callsArgWith(2, null, ctx.documentclassContent)
|
||||
})
|
||||
|
||||
it('returns main.tex', async function () {
|
||||
it('returns main.tex', async function (ctx) {
|
||||
const { path, content } =
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(path).to.equal('main.tex')
|
||||
expect(content).to.equal(this.documentclassContent)
|
||||
expect(content).to.equal(ctx.documentclassContent)
|
||||
})
|
||||
|
||||
it('processes main.text first and stops processing when it finds the content', async function () {
|
||||
await this.ProjectRootDocManager.findRootDocFileFromDirectory('/foo')
|
||||
expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
|
||||
expect(this.fs.readFile).not.to.be.calledWith('/foo/a.tex')
|
||||
it('processes main.text first and stops processing when it finds the content', async function (ctx) {
|
||||
await ctx.ProjectRootDocManager.findRootDocFileFromDirectory('/foo')
|
||||
expect(ctx.fs.readFile).to.be.calledWith('/foo/main.tex')
|
||||
expect(ctx.fs.readFile).not.to.be.calledWith('/foo/a.tex')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when main.tex does not contain a line starting with \\documentclass', function () {
|
||||
beforeEach(function () {
|
||||
this.fs.readFile.withArgs('/foo/a.tex').callsArgWith(2, null, 'foo')
|
||||
this.fs.readFile.withArgs('/foo/main.tex').callsArgWith(2, null, 'foo')
|
||||
this.fs.readFile.withArgs('/foo/z.tex').callsArgWith(2, null, 'foo')
|
||||
this.fs.readFile
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fs.readFile.withArgs('/foo/a.tex').callsArgWith(2, null, 'foo')
|
||||
ctx.fs.readFile.withArgs('/foo/main.tex').callsArgWith(2, null, 'foo')
|
||||
ctx.fs.readFile.withArgs('/foo/z.tex').callsArgWith(2, null, 'foo')
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/nested/chapter1a.tex')
|
||||
.callsArgWith(2, null, 'foo')
|
||||
})
|
||||
|
||||
it('returns the first .tex file from the root folder', async function () {
|
||||
this.globbyFiles.splice(
|
||||
it('returns the first .tex file from the root folder', async function (ctx) {
|
||||
ctx.globbyFiles.splice(
|
||||
0,
|
||||
this.globbyFiles.length,
|
||||
ctx.globbyFiles.length,
|
||||
'a.tex',
|
||||
'z.tex',
|
||||
'nested/chapter1a.tex'
|
||||
)
|
||||
|
||||
const { path, content } =
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(path).to.equal('a.tex')
|
||||
expect(content).to.equal('foo')
|
||||
})
|
||||
|
||||
it('returns main.tex file from the root folder', async function () {
|
||||
this.globbyFiles.splice(
|
||||
it('returns main.tex file from the root folder', async function (ctx) {
|
||||
ctx.globbyFiles.splice(
|
||||
0,
|
||||
this.globbyFiles.length,
|
||||
ctx.globbyFiles.length,
|
||||
'a.tex',
|
||||
'z.tex',
|
||||
'main.tex',
|
||||
@@ -277,7 +298,7 @@ describe('ProjectRootDocManager', function () {
|
||||
)
|
||||
|
||||
const { path, content } =
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(path).to.equal('main.tex')
|
||||
@@ -286,60 +307,60 @@ describe('ProjectRootDocManager', function () {
|
||||
})
|
||||
|
||||
describe('when a.tex contains a documentclass', function () {
|
||||
beforeEach(function () {
|
||||
this.fs.readFile
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/a.tex')
|
||||
.callsArgWith(2, null, this.documentclassContent)
|
||||
.callsArgWith(2, null, ctx.documentclassContent)
|
||||
})
|
||||
|
||||
it('returns a.tex', async function () {
|
||||
it('returns a.tex', async function (ctx) {
|
||||
const { path, content } =
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(path).to.equal('a.tex')
|
||||
expect(content).to.equal(this.documentclassContent)
|
||||
expect(content).to.equal(ctx.documentclassContent)
|
||||
})
|
||||
|
||||
it('processes main.text first and stops processing when it finds the content', async function () {
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
it('processes main.text first and stops processing when it finds the content', async function (ctx) {
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
|
||||
expect(this.fs.readFile).to.be.calledWith('/foo/a.tex')
|
||||
expect(this.fs.readFile).not.to.be.calledWith('/foo/b.tex')
|
||||
expect(ctx.fs.readFile).to.be.calledWith('/foo/main.tex')
|
||||
expect(ctx.fs.readFile).to.be.calledWith('/foo/a.tex')
|
||||
expect(ctx.fs.readFile).not.to.be.calledWith('/foo/b.tex')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is no documentclass', function () {
|
||||
it('returns with no error', async function () {
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
it('returns with no error', async function (ctx) {
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
})
|
||||
|
||||
it('processes all the files', async function () {
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
it('processes all the files', async function (ctx) {
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
|
||||
expect(this.fs.readFile).to.be.calledWith('/foo/a.tex')
|
||||
expect(this.fs.readFile).to.be.calledWith('/foo/b.tex')
|
||||
expect(ctx.fs.readFile).to.be.calledWith('/foo/main.tex')
|
||||
expect(ctx.fs.readFile).to.be.calledWith('/foo/a.tex')
|
||||
expect(ctx.fs.readFile).to.be.calledWith('/foo/b.tex')
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is an error reading a file', function () {
|
||||
beforeEach(function () {
|
||||
this.fs.readFile
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fs.readFile
|
||||
.withArgs('/foo/a.tex')
|
||||
.callsArgWith(2, new Error('something went wrong'))
|
||||
})
|
||||
|
||||
it('returns an error', async function () {
|
||||
it('returns an error', async function (ctx) {
|
||||
let error
|
||||
|
||||
try {
|
||||
await this.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
await ctx.ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
'/foo'
|
||||
)
|
||||
} catch (err) {
|
||||
@@ -353,206 +374,196 @@ describe('ProjectRootDocManager', function () {
|
||||
|
||||
describe('setRootDocFromName', function () {
|
||||
describe('when there is a suitable root doc', function () {
|
||||
beforeEach(async function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docPaths)
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon
|
||||
.stub()
|
||||
.callsArgWith(2)
|
||||
await this.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
this.project_id,
|
||||
.callsArgWith(1, null, ctx.docPaths)
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
ctx.project_id,
|
||||
'/main.tex'
|
||||
)
|
||||
})
|
||||
|
||||
it('should check the docs of the project', function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(this.project_id)
|
||||
it('should check the docs of the project', function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should set the root doc to main.tex', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(this.project_id, this.docId2.toString())
|
||||
it('should set the root doc to main.tex', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(ctx.project_id, ctx.docId2.toString())
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is a suitable root doc but the leading slash is missing', function () {
|
||||
beforeEach(async function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docPaths)
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon
|
||||
.stub()
|
||||
.callsArgWith(2)
|
||||
await this.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
this.project_id,
|
||||
.callsArgWith(1, null, ctx.docPaths)
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
ctx.project_id,
|
||||
'main.tex'
|
||||
)
|
||||
})
|
||||
|
||||
it('should check the docs of the project', function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(this.project_id)
|
||||
it('should check the docs of the project', function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should set the root doc to main.tex', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(this.project_id, this.docId2.toString())
|
||||
it('should set the root doc to main.tex', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(ctx.project_id, ctx.docId2.toString())
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is a suitable root doc with a basename match', function () {
|
||||
beforeEach(async function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docPaths)
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon
|
||||
.stub()
|
||||
.callsArgWith(2)
|
||||
await this.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
this.project_id,
|
||||
.callsArgWith(1, null, ctx.docPaths)
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
ctx.project_id,
|
||||
'chapter1a.tex'
|
||||
)
|
||||
})
|
||||
|
||||
it('should check the docs of the project', function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(this.project_id)
|
||||
it('should check the docs of the project', function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should set the root doc using the basename', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(this.project_id, this.docId3.toString())
|
||||
it('should set the root doc using the basename', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(ctx.project_id, ctx.docId3.toString())
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is a suitable root doc but the filename is in quotes', function () {
|
||||
beforeEach(async function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docPaths)
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon
|
||||
.stub()
|
||||
.callsArgWith(2)
|
||||
await this.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
this.project_id,
|
||||
.callsArgWith(1, null, ctx.docPaths)
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
ctx.project_id,
|
||||
"'main.tex'"
|
||||
)
|
||||
})
|
||||
|
||||
it('should check the docs of the project', function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(this.project_id)
|
||||
it('should check the docs of the project', function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should set the root doc to main.tex', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(this.project_id, this.docId2.toString())
|
||||
it('should set the root doc to main.tex', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc
|
||||
.calledWith(ctx.project_id, ctx.docId2.toString())
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is no suitable root doc', function () {
|
||||
beforeEach(async function () {
|
||||
this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.docPaths)
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon
|
||||
.stub()
|
||||
.callsArgWith(2)
|
||||
await this.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
this.project_id,
|
||||
.callsArgWith(1, null, ctx.docPaths)
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
|
||||
await ctx.ProjectRootDocManager.promises.setRootDocFromName(
|
||||
ctx.project_id,
|
||||
'other.tex'
|
||||
)
|
||||
})
|
||||
|
||||
it('should not set the root doc', function () {
|
||||
this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(false)
|
||||
it('should not set the root doc', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ensureRootDocumentIsSet', function () {
|
||||
beforeEach(function () {
|
||||
this.project = {}
|
||||
this.ProjectGetter.getProject = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project = {}
|
||||
ctx.ProjectGetter.getProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, null, this.project)
|
||||
this.ProjectRootDocManager.setRootDocAutomatically = sinon
|
||||
.callsArgWith(2, null, ctx.project)
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null)
|
||||
})
|
||||
|
||||
describe('when the root doc is set', function () {
|
||||
beforeEach(function () {
|
||||
this.project.rootDoc_id = this.docId2
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsSet(
|
||||
this.project_id,
|
||||
this.callback
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project.rootDoc_id = ctx.docId2
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsSet(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the project fetching only the rootDoc_id field', function () {
|
||||
this.ProjectGetter.getProject
|
||||
.calledWith(this.project_id, { rootDoc_id: 1 })
|
||||
it('should find the project fetching only the rootDoc_id field', function (ctx) {
|
||||
ctx.ProjectGetter.getProject
|
||||
.calledWith(ctx.project_id, { rootDoc_id: 1 })
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should not try to update the project rootDoc_id', function () {
|
||||
this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
|
||||
it('should not try to update the project rootDoc_id', function (ctx) {
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('should call the callback', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
it('should call the callback', function (ctx) {
|
||||
ctx.callback.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the root doc is not set', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsSet(
|
||||
this.project_id,
|
||||
this.callback
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsSet(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the project with only the rootDoc_id field', function () {
|
||||
this.ProjectGetter.getProject
|
||||
.calledWith(this.project_id, { rootDoc_id: 1 })
|
||||
it('should find the project with only the rootDoc_id field', function (ctx) {
|
||||
ctx.ProjectGetter.getProject
|
||||
.calledWith(ctx.project_id, { rootDoc_id: 1 })
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should update the project rootDoc_id', function () {
|
||||
this.ProjectRootDocManager.setRootDocAutomatically
|
||||
.calledWith(this.project_id)
|
||||
it('should update the project rootDoc_id', function (ctx) {
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call the callback', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
it('should call the callback', function (ctx) {
|
||||
ctx.callback.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the project does not exist', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsSet(
|
||||
this.project_id,
|
||||
this.callback
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsSet(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should call the callback with an error', function () {
|
||||
this.callback
|
||||
it('should call the callback with an error', function (ctx) {
|
||||
ctx.callback
|
||||
.calledWith(
|
||||
sinon.match
|
||||
.instanceOf(Error)
|
||||
@@ -564,125 +575,125 @@ describe('ProjectRootDocManager', function () {
|
||||
})
|
||||
|
||||
describe('ensureRootDocumentIsValid', function () {
|
||||
beforeEach(function () {
|
||||
this.project = {}
|
||||
this.ProjectGetter.getProject = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project = {}
|
||||
ctx.ProjectGetter.getProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, null, this.project)
|
||||
this.ProjectGetter.getProjectWithoutDocLines = sinon
|
||||
.callsArgWith(2, null, ctx.project)
|
||||
ctx.ProjectGetter.getProjectWithoutDocLines = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.project)
|
||||
this.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().yields()
|
||||
this.ProjectEntityUpdateHandler.unsetRootDoc = sinon.stub().yields()
|
||||
this.ProjectRootDocManager.setRootDocAutomatically = sinon
|
||||
.callsArgWith(1, null, ctx.project)
|
||||
ctx.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().yields()
|
||||
ctx.ProjectEntityUpdateHandler.unsetRootDoc = sinon.stub().yields()
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null)
|
||||
})
|
||||
|
||||
describe('when the root doc is set', function () {
|
||||
describe('when the root doc is valid', function () {
|
||||
beforeEach(function () {
|
||||
this.project.rootDoc_id = this.docId2
|
||||
this.ProjectEntityHandler.getDocPathFromProjectByDocId = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project.rootDoc_id = ctx.docId2
|
||||
ctx.ProjectEntityHandler.getDocPathFromProjectByDocId = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, null, this.docPaths[this.docId2])
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
this.project_id,
|
||||
this.callback
|
||||
.callsArgWith(2, null, ctx.docPaths[ctx.docId2])
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the project without doc lines', function () {
|
||||
this.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(this.project_id)
|
||||
it('should find the project without doc lines', function (ctx) {
|
||||
ctx.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should not try to update the project rootDoc_id', function () {
|
||||
this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
|
||||
it('should not try to update the project rootDoc_id', function (ctx) {
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('should call the callback', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
it('should call the callback', function (ctx) {
|
||||
ctx.callback.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the root doc is not valid', function () {
|
||||
beforeEach(function () {
|
||||
this.project.rootDoc_id = new ObjectId()
|
||||
this.ProjectEntityHandler.getDocPathFromProjectByDocId = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project.rootDoc_id = new ObjectId()
|
||||
ctx.ProjectEntityHandler.getDocPathFromProjectByDocId = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, null, null)
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
this.project_id,
|
||||
this.callback
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the project without doc lines', function () {
|
||||
this.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(this.project_id)
|
||||
it('should find the project without doc lines', function (ctx) {
|
||||
ctx.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should unset the root doc', function () {
|
||||
this.ProjectEntityUpdateHandler.unsetRootDoc
|
||||
.calledWith(this.project_id)
|
||||
it('should unset the root doc', function (ctx) {
|
||||
ctx.ProjectEntityUpdateHandler.unsetRootDoc
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should try to find a new rootDoc', function () {
|
||||
this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
|
||||
it('should try to find a new rootDoc', function (ctx) {
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('should call the callback', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
it('should call the callback', function (ctx) {
|
||||
ctx.callback.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the root doc is not set', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
this.project_id,
|
||||
this.callback
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the project without doc lines', function () {
|
||||
this.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(this.project_id)
|
||||
it('should find the project without doc lines', function (ctx) {
|
||||
ctx.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should update the project rootDoc_id', function () {
|
||||
this.ProjectRootDocManager.setRootDocAutomatically
|
||||
.calledWith(this.project_id)
|
||||
it('should update the project rootDoc_id', function (ctx) {
|
||||
ctx.ProjectRootDocManager.setRootDocAutomatically
|
||||
.calledWith(ctx.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call the callback', function () {
|
||||
this.callback.called.should.equal(true)
|
||||
it('should call the callback', function (ctx) {
|
||||
ctx.callback.called.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the project does not exist', function () {
|
||||
beforeEach(function () {
|
||||
this.ProjectGetter.getProjectWithoutDocLines = sinon
|
||||
beforeEach(function (ctx) {
|
||||
ctx.ProjectGetter.getProjectWithoutDocLines = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, null)
|
||||
this.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
this.project_id,
|
||||
this.callback
|
||||
ctx.ProjectRootDocManager.ensureRootDocumentIsValid(
|
||||
ctx.project_id,
|
||||
ctx.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should call the callback with an error', function () {
|
||||
this.callback
|
||||
it('should call the callback with an error', function (ctx) {
|
||||
ctx.callback
|
||||
.calledWith(
|
||||
sinon.match
|
||||
.instanceOf(Error)
|
||||
|
||||
Reference in New Issue
Block a user