mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-24 15:49:42 +02:00
Beautiful urls (#18)
* Added uri property to Page model * Added aliases collection * Added routing form aliases * Fixed redirect after page creation * Added abiltity to support few pages with same title * Added ability to change uri manually * Changed hash function * Changed uri parsing * Removed pages controller promise * Modified page's tests * Added tests for alias model * Added tests for aliases * Escaping special characters * Added missed files * Fixed bugs related to translation * Fixed parent page link * Added server validation for uri * Changed css properties order * Made uri property of page be optional * Prevented alias creation from empty uri * Moved alias types to model
This commit is contained in:
parent
ed69336481
commit
d872e78339
28 changed files with 807 additions and 45 deletions
139
test/models/alias.js
Normal file
139
test/models/alias.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
const {expect} = require('chai');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const config = require('../../config');
|
||||
const Alias = require('../../src/models/alias');
|
||||
const binaryMD5 = require('../../src/utils/crypto');
|
||||
const {aliases} = require('../../src/utils/database');
|
||||
|
||||
describe('Alias model', () => {
|
||||
after(() => {
|
||||
const pathToDB = path.resolve(__dirname, '../../', config.database, './aliases.db');
|
||||
|
||||
if (fs.existsSync(pathToDB)) {
|
||||
fs.unlinkSync(pathToDB);
|
||||
}
|
||||
});
|
||||
|
||||
it('Working with empty model', async () => {
|
||||
let alias = new Alias();
|
||||
|
||||
expect(alias.data).to.be.a('object');
|
||||
|
||||
let {data} = alias;
|
||||
|
||||
expect(data._id).to.be.undefined;
|
||||
expect(data.hash).to.be.undefined;
|
||||
expect(data.type).to.be.undefined;
|
||||
expect(data.deprecated).to.be.false;
|
||||
expect(data.id).to.be.undefined;
|
||||
|
||||
alias = new Alias();
|
||||
|
||||
data = alias.data;
|
||||
|
||||
expect(data._id).to.be.undefined;
|
||||
expect(data.hash).to.be.undefined;
|
||||
expect(data.type).to.be.undefined;
|
||||
expect(data.deprecated).to.be.false;
|
||||
expect(data.id).to.be.undefined;
|
||||
|
||||
const initialData = {
|
||||
_id: 'alias_id',
|
||||
type: Alias.types.PAGE,
|
||||
id: 'page_id'
|
||||
};
|
||||
const aliasName = 'alias name';
|
||||
|
||||
alias = new Alias(initialData, aliasName);
|
||||
data = alias.data;
|
||||
|
||||
expect(data._id).to.equal(initialData._id);
|
||||
expect(data.hash).to.equal(binaryMD5(aliasName));
|
||||
expect(data.type).to.equal(initialData.type);
|
||||
expect(data.deprecated).to.equal(false);
|
||||
|
||||
const update = {
|
||||
type: Alias.types.PAGE,
|
||||
id: 'page_id',
|
||||
hash: binaryMD5('another test hash'),
|
||||
deprecated: true
|
||||
};
|
||||
|
||||
alias.data = update;
|
||||
|
||||
data = alias.data;
|
||||
|
||||
expect(data._id).to.equal(initialData._id);
|
||||
expect(data.type).to.equal(update.type);
|
||||
expect(data.hash).to.equal(update.hash);
|
||||
expect(data.deprecated).to.equal(update.deprecated);
|
||||
});
|
||||
|
||||
it('Static get method', async () => {
|
||||
const initialData = {
|
||||
type: Alias.types.PAGE,
|
||||
id: 'page_id'
|
||||
};
|
||||
const aliasName = 'alias name';
|
||||
|
||||
const alias = new Alias(initialData, aliasName);
|
||||
|
||||
const savedAlias = await alias.save();
|
||||
|
||||
const foundAlias = await Alias.get(aliasName);
|
||||
|
||||
const {data} = foundAlias;
|
||||
|
||||
expect(data._id).to.equal(savedAlias._id);
|
||||
expect(data.hash).to.equal(binaryMD5(aliasName));
|
||||
expect(data.type).to.equal(initialData.type);
|
||||
expect(data.deprecated).to.equal(false);
|
||||
});
|
||||
|
||||
it('Saving, updating and deleting model in the database', async () => {
|
||||
const initialData = {
|
||||
type: Alias.types.PAGE,
|
||||
id: 'page_id'
|
||||
};
|
||||
const aliasName = 'alias name';
|
||||
|
||||
const alias = new Alias(initialData, aliasName);
|
||||
|
||||
const savedAlias = await alias.save();
|
||||
|
||||
expect(savedAlias._id).not.be.undefined;
|
||||
expect(savedAlias.hash).to.equal(binaryMD5(aliasName));
|
||||
expect(savedAlias.type).to.equal(initialData.type);
|
||||
expect(savedAlias.id).to.equal(initialData.id);
|
||||
expect(savedAlias.deprecated).to.equal(false);
|
||||
|
||||
const insertedAlias = await aliases.findOne({_id: savedAlias._id});
|
||||
|
||||
expect(insertedAlias._id).to.equal(savedAlias._id);
|
||||
expect(insertedAlias.hash).to.equal(savedAlias.hash);
|
||||
expect(insertedAlias.type).to.equal(savedAlias.type);
|
||||
expect(insertedAlias.id).to.equal(savedAlias.id);
|
||||
expect(insertedAlias.deprecated).to.equal(savedAlias.deprecated);
|
||||
|
||||
const updateData = {
|
||||
type: Alias.types.PAGE,
|
||||
id: 'page_id',
|
||||
hash: binaryMD5('another test hash'),
|
||||
deprecated: true
|
||||
};
|
||||
|
||||
alias.data = updateData;
|
||||
await alias.save();
|
||||
|
||||
expect(alias._id).to.equal(insertedAlias._id);
|
||||
|
||||
const updatedAlias = await aliases.findOne({_id: alias._id});
|
||||
|
||||
expect(updatedAlias._id).to.equal(savedAlias._id);
|
||||
expect(updatedAlias.hash).to.equal(updateData.hash);
|
||||
expect(updatedAlias.type).to.equal(updateData.type);
|
||||
expect(updatedAlias.id).to.equal(updateData.id);
|
||||
expect(updatedAlias.deprecated).to.equal(updateData.deprecated);
|
||||
});
|
||||
});
|
|
@ -4,8 +4,21 @@ const path = require('path');
|
|||
const config = require('../../config');
|
||||
const Page = require('../../src/models/page');
|
||||
const {pages} = require('../../src/utils/database');
|
||||
const translateString = require('../../src/utils/translation');
|
||||
|
||||
describe('Page model', () => {
|
||||
|
||||
const transformToUri = (string) => {
|
||||
return translateString(string
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ')
|
||||
.replace(/ +/g, ' ')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.join('-'));
|
||||
};
|
||||
|
||||
after(() => {
|
||||
const pathToDB = path.resolve(__dirname, '../../', config.database, './pages.db');
|
||||
|
||||
|
@ -23,6 +36,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(data._id).to.be.undefined;
|
||||
expect(data.title).to.be.empty;
|
||||
expect(data.uri).to.be.empty;
|
||||
expect(data.body).to.be.undefined;
|
||||
expect(data.parent).to.be.undefined;
|
||||
|
||||
|
@ -32,6 +46,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(data._id).to.be.undefined;
|
||||
expect(data.title).to.be.empty;
|
||||
expect(data.uri).to.be.empty;
|
||||
expect(data.body).to.be.undefined;
|
||||
expect(data.parent).to.be.undefined;
|
||||
|
||||
|
@ -57,11 +72,13 @@ describe('Page model', () => {
|
|||
|
||||
expect(data._id).to.equal(initialData._id);
|
||||
expect(data.title).to.equal(initialData.body.blocks[0].data.text);
|
||||
expect(data.uri).to.be.empty;
|
||||
expect(data.body).to.deep.equal(initialData.body);
|
||||
expect(data.parent).to.be.undefined;
|
||||
|
||||
expect(json._id).to.equal(initialData._id);
|
||||
expect(json.title).to.equal(initialData.body.blocks[0].data.text);
|
||||
expect(json.title).to.equal(initialData.body.blocks[0].data.text);
|
||||
expect(json.body).to.deep.equal(initialData.body);
|
||||
expect(json.parent).to.be.undefined;
|
||||
|
||||
|
@ -85,6 +102,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(data._id).to.equal(initialData._id);
|
||||
expect(data.title).to.equal(update.body.blocks[0].data.text);
|
||||
expect(data.uri).to.be.empty;
|
||||
expect(data.body).to.equal(update.body);
|
||||
expect(data.parent).to.be.undefined;
|
||||
});
|
||||
|
@ -96,7 +114,7 @@ describe('Page model', () => {
|
|||
{
|
||||
type: 'header',
|
||||
data: {
|
||||
text: 'Page header'
|
||||
text: 'New page header'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -108,6 +126,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(savedPage._id).not.be.undefined;
|
||||
expect(savedPage.title).to.equal(initialData.body.blocks[0].data.text);
|
||||
expect(savedPage.uri).to.equal(transformToUri(initialData.body.blocks[0].data.text));
|
||||
expect(savedPage.body).to.equal(initialData.body);
|
||||
expect(page._id).not.be.undefined;
|
||||
|
||||
|
@ -115,6 +134,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(insertedPage._id).to.equal(page._id);
|
||||
expect(insertedPage.title).to.equal(page.title);
|
||||
expect(insertedPage.uri).to.equal(page.uri);
|
||||
expect(insertedPage.body).to.deep.equal(page.body);
|
||||
|
||||
const updateData = {
|
||||
|
@ -127,7 +147,8 @@ describe('Page model', () => {
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
uri: 'updated-uri'
|
||||
};
|
||||
|
||||
page.data = updateData;
|
||||
|
@ -139,6 +160,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(updatedPage._id).to.equal(savedPage._id);
|
||||
expect(updatedPage.title).to.equal(updateData.body.blocks[0].data.text);
|
||||
expect(updatedPage.uri).to.equal(updateData.uri);
|
||||
expect(updatedPage.body).to.deep.equal(updateData.body);
|
||||
|
||||
await page.destroy();
|
||||
|
@ -150,6 +172,39 @@ describe('Page model', () => {
|
|||
expect(removedPage).to.be.null;
|
||||
});
|
||||
|
||||
it('Handle multiple page creation with the same uri', async () => {
|
||||
const initialData = {
|
||||
body: {
|
||||
blocks: [
|
||||
{
|
||||
type: 'header',
|
||||
data: {
|
||||
text: 'New page header'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const firstPage = new Page(initialData);
|
||||
let firstSavedPage = await firstPage.save();
|
||||
const secondPage = new Page(initialData);
|
||||
let secondSavedPage = await secondPage.save();
|
||||
|
||||
expect(secondSavedPage.uri).to.equal(transformToUri(initialData.body.blocks[0].data.text) + '-1');
|
||||
|
||||
const newUri = 'new-uri';
|
||||
|
||||
firstPage.data = {...firstPage.data, uri: newUri};
|
||||
firstSavedPage = await firstPage.save();
|
||||
|
||||
expect(firstSavedPage.uri).to.equal(newUri);
|
||||
|
||||
const thirdPage = new Page(initialData);
|
||||
let thirdSavedPage = await thirdPage.save();
|
||||
|
||||
expect(thirdSavedPage.uri).to.equal(transformToUri(initialData.body.blocks[0].data.text));
|
||||
});
|
||||
|
||||
it('Static get method', async () => {
|
||||
const initialData = {
|
||||
body: {
|
||||
|
@ -173,6 +228,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(data._id).to.equal(savedPage._id);
|
||||
expect(data.title).to.equal(initialData.body.blocks[0].data.text);
|
||||
expect(data.uri).to.equal(transformToUri(initialData.body.blocks[0].data.text));
|
||||
expect(data.body).to.deep.equal(initialData.body);
|
||||
|
||||
await page.destroy();
|
||||
|
@ -213,6 +269,7 @@ describe('Page model', () => {
|
|||
expect(foundPages.length).to.equal(2);
|
||||
foundPages.forEach((page, i) => {
|
||||
expect(page.title).to.equal(pagesToSave[i].body.blocks[0].data.text);
|
||||
expect(page.uri).to.equal(transformToUri(pagesToSave[i].body.blocks[0].data.text));
|
||||
expect(page.body).to.deep.equal(pagesToSave[i].body);
|
||||
});
|
||||
});
|
||||
|
@ -257,6 +314,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(testedParent._id).to.equal(parentId);
|
||||
expect(testedParent.title).to.equal(parent.body.blocks[0].data.text);
|
||||
expect(testedParent.uri).to.equal(transformToUri(parent.body.blocks[0].data.text));
|
||||
expect(testedParent.body).to.deep.equal(parent.body);
|
||||
|
||||
const children = await parent.children;
|
||||
|
@ -267,6 +325,7 @@ describe('Page model', () => {
|
|||
|
||||
expect(testedChild._id).to.equal(childId);
|
||||
expect(testedChild.title).to.equal(child.body.blocks[0].data.text);
|
||||
expect(testedChild.uri).to.equal(transformToUri(child.body.blocks[0].data.text));
|
||||
expect(testedChild.body).to.deep.equal(child.body);
|
||||
expect(testedChild._parent).to.equal(child._parent);
|
||||
expect(testedChild._parent).to.equal(parent._id);
|
||||
|
|
58
test/rest/aliases.js
Normal file
58
test/rest/aliases.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
const {app} = require('../../bin/www');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const config = require('../../config');
|
||||
const chai = require('chai');
|
||||
const chaiHTTP = require('chai-http');
|
||||
const {expect} = chai;
|
||||
|
||||
chai.use(chaiHTTP);
|
||||
|
||||
describe('Aliases REST: ', () => {
|
||||
let agent;
|
||||
|
||||
before(async () => {
|
||||
agent = chai.request.agent(app);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
const pathToDB = path.resolve(__dirname, '../../', config.database, './pages.db');
|
||||
|
||||
if (fs.existsSync(pathToDB)) {
|
||||
fs.unlinkSync(pathToDB);
|
||||
}
|
||||
|
||||
const pathToAliasDB = path.resolve(__dirname, '../../', config.database, './aliases.db');
|
||||
|
||||
if (fs.existsSync(pathToAliasDB)) {
|
||||
fs.unlinkSync(pathToAliasDB);
|
||||
}
|
||||
});
|
||||
|
||||
it('Finding page with alias', async () => {
|
||||
const body = {
|
||||
blocks: [
|
||||
{
|
||||
type: 'header',
|
||||
data: {
|
||||
text: 'Test header'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const put = await agent
|
||||
.put('/api/page')
|
||||
.send({body});
|
||||
|
||||
expect(put).to.have.status(200);
|
||||
expect(put).to.be.json;
|
||||
|
||||
const {result: {uri}} = put.body;
|
||||
|
||||
const get = await agent.get(`/${uri}`);
|
||||
|
||||
expect(get).to.have.status(200);
|
||||
});
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
const {app} = require('../../bin/www');
|
||||
const model = require('../../src/models/page');
|
||||
const PageOrder = require('../../src/models/pageOrder');
|
||||
const translateString = require('../../src/utils/translation');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
@ -13,6 +14,16 @@ chai.use(chaiHTTP);
|
|||
|
||||
describe('Pages REST: ', () => {
|
||||
let agent;
|
||||
const transformToUri = (string) => {
|
||||
return translateString(string
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ')
|
||||
.replace(/ +/g, ' ')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.join('-'));
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
agent = chai.request.agent(app);
|
||||
|
@ -35,9 +46,9 @@ describe('Pages REST: ', () => {
|
|||
text: 'Page header'
|
||||
}
|
||||
}
|
||||
],
|
||||
]
|
||||
};
|
||||
const parent = 0;
|
||||
const parent = 0;
|
||||
const res = await agent
|
||||
.put('/api/page')
|
||||
.send({body, parent});
|
||||
|
@ -50,6 +61,7 @@ describe('Pages REST: ', () => {
|
|||
expect(success).to.be.true;
|
||||
expect(result._id).to.be.a('string');
|
||||
expect(result.title).to.equal(body.blocks[0].data.text);
|
||||
expect(result.uri).to.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(result.body).to.deep.equal(body);
|
||||
|
||||
const createdPage = await model.get(result._id);
|
||||
|
@ -57,13 +69,14 @@ describe('Pages REST: ', () => {
|
|||
expect(createdPage).not.be.null;
|
||||
expect(createdPage._id).to.equal(result._id);
|
||||
expect(createdPage.title).to.equal(body.blocks[0].data.text);
|
||||
expect(createdPage.uri).to.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(createdPage.body).to.deep.equal(body);
|
||||
|
||||
const pageOrder = await PageOrder.get('' + (createdPage.data.parent || 0));
|
||||
expect(pageOrder.order).to.be.an('array');
|
||||
|
||||
await createdPage.destroy();
|
||||
await pageOrder.destroy()
|
||||
await pageOrder.destroy();
|
||||
});
|
||||
|
||||
it('Page data validation on create', async () => {
|
||||
|
@ -115,6 +128,7 @@ describe('Pages REST: ', () => {
|
|||
|
||||
expect(foundPage._id).to.equal(_id);
|
||||
expect(foundPage.title).to.equal(body.blocks[0].data.text);
|
||||
expect(foundPage.uri).to.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(foundPage.body).to.deep.equal(body);
|
||||
|
||||
await pageOrder.destroy();
|
||||
|
@ -164,10 +178,11 @@ describe('Pages REST: ', () => {
|
|||
}
|
||||
]
|
||||
};
|
||||
const updatedUri = 'updated-uri';
|
||||
|
||||
res = await agent
|
||||
.post(`/api/page/${_id}`)
|
||||
.send({body: updatedBody});
|
||||
.send({body: updatedBody, uri: updatedUri});
|
||||
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
|
@ -178,6 +193,8 @@ describe('Pages REST: ', () => {
|
|||
expect(result._id).to.equal(_id);
|
||||
expect(result.title).not.equal(body.blocks[0].data.text);
|
||||
expect(result.title).to.equal(updatedBody.blocks[0].data.text);
|
||||
expect(result.uri).not.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(result.uri).to.equal(updatedUri);
|
||||
expect(result.body).not.equal(body);
|
||||
expect(result.body).to.deep.equal(updatedBody);
|
||||
|
||||
|
@ -187,6 +204,8 @@ describe('Pages REST: ', () => {
|
|||
expect(updatedPage._id).to.equal(_id);
|
||||
expect(updatedPage.title).not.equal(body.blocks[0].data.text);
|
||||
expect(updatedPage.title).to.equal(updatedBody.blocks[0].data.text);
|
||||
expect(updatedPage.uri).not.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(updatedPage.uri).to.equal(updatedUri);
|
||||
expect(updatedPage.body).not.equal(body);
|
||||
expect(updatedPage.body).to.deep.equal(updatedBody);
|
||||
|
||||
|
@ -194,6 +213,65 @@ describe('Pages REST: ', () => {
|
|||
await updatedPage.destroy();
|
||||
});
|
||||
|
||||
it('Handle multiple page creation with the same uri', async () => {
|
||||
const body = {
|
||||
blocks: [
|
||||
{
|
||||
type: 'header',
|
||||
data: {
|
||||
text: 'Page header'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let res = await agent
|
||||
.put('/api/page')
|
||||
.send({body});
|
||||
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
|
||||
const {result: {_id}} = res.body;
|
||||
|
||||
res = await agent
|
||||
.put('/api/page')
|
||||
.send({body: body});
|
||||
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
|
||||
const {success: secondPageSuccess, result: secondPageResult} = res.body;
|
||||
|
||||
expect(secondPageSuccess).to.be.true;
|
||||
expect(secondPageResult.title).to.equal(body.blocks[0].data.text);
|
||||
expect(secondPageResult.uri).to.equal(transformToUri(body.blocks[0].data.text) + '-1');
|
||||
expect(secondPageResult.body).to.deep.equal(body);
|
||||
|
||||
const newFirstPageUri = 'New-uri';
|
||||
|
||||
res = await agent
|
||||
.post(`/api/page/${_id}`)
|
||||
.send({body: body, uri: newFirstPageUri});
|
||||
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
|
||||
res = await agent
|
||||
.put('/api/page')
|
||||
.send({body: body});
|
||||
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
|
||||
const {success: thirdPageSuccess, result: thirdPageResult} = res.body;
|
||||
|
||||
expect(thirdPageSuccess).to.be.true;
|
||||
expect(thirdPageResult.title).to.equal(body.blocks[0].data.text);
|
||||
expect(thirdPageResult.uri).to.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(thirdPageResult.body).to.deep.equal(body);
|
||||
});
|
||||
|
||||
it('Updating page with not existing id', async () => {
|
||||
const res = await agent
|
||||
.post('/api/page/not-existing-id')
|
||||
|
@ -223,7 +301,7 @@ describe('Pages REST: ', () => {
|
|||
{
|
||||
type: 'header',
|
||||
data: {
|
||||
text: 'Page header'
|
||||
text: 'Page header to be deleted'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -249,6 +327,7 @@ describe('Pages REST: ', () => {
|
|||
expect(success).to.be.true;
|
||||
expect(result._id).to.be.undefined;
|
||||
expect(result.title).to.equal(body.blocks[0].data.text);
|
||||
expect(result.uri).to.equal(transformToUri(body.blocks[0].data.text));
|
||||
expect(result.body).to.deep.equal(body);
|
||||
|
||||
const deletedPage = await model.get(_id);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue