1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-08-04 13:05:22 +02:00

Typescript rewrite (#147)

* Updated highlight.js

* Update .codexdocsrc.sample

remove undefined page for a fresh new install

* backend rewritten in TS

* test -> TS, .added dockerignore, bug fixed

* Removed compiled js files, eslint codex/ts added

* fixed jsdocs warning, leaving editor confirmation

* use path.resolve for DB paths

* db drives updated + fixed User model

* redundant cleared + style fixed

* explicit type fixing

* fixing testing code

* added body block type

* compiled JS files -> dist, fixed compiling errors

* fixed compiling error, re-organized ts source code

* updated Dockerfile

* fixed link to parent page

* up nodejs version

* fix package name

* fix deps

Co-authored-by: nvc8996 <nvc.8996@gmail.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
This commit is contained in:
Nikita Melnikov 2022-03-05 22:57:23 +04:00 committed by GitHub
parent 059cfb96f9
commit 34514761f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
99 changed files with 3817 additions and 2249 deletions

141
src/test/models/alias.ts Normal file
View file

@ -0,0 +1,141 @@
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';
import config from 'config';
import Alias from '../../backend/models/alias';
import { binaryMD5 } from '../../backend/utils/crypto';
import database from '../../backend/utils/database';
const aliases = database['aliases'];
describe('Alias model', () => {
after(() => {
const pathToDB = path.resolve(__dirname, '../../../', config.get('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}) as Alias;
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}) as Alias;
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);
});
});

237
src/test/models/file.ts Normal file
View file

@ -0,0 +1,237 @@
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';
import config from 'config';
import File from '../../backend/models/file';
import database from '../../backend/utils/database';
const files = database['files'];
describe('File model', () => {
after(() => {
const pathToDB = path.resolve(__dirname, '../../../', config.get('database'), './files.db');
if (fs.existsSync(pathToDB)) {
fs.unlinkSync(pathToDB);
}
});
it('Working with empty model', async () => {
let file = new File();
expect(file.data).to.be.a('object');
let { data } = file;
expect(data._id).to.be.undefined;
expect(data.name).to.be.undefined;
expect(data.filename).to.be.undefined;
expect(data.path).to.be.undefined;
expect(data.size).to.be.undefined;
expect(data.mimetype).to.be.undefined;
file = new File();
data = file.data;
expect(data._id).to.be.undefined;
expect(data.name).to.be.undefined;
expect(data.filename).to.be.undefined;
expect(data.path).to.be.undefined;
expect(data.size).to.be.undefined;
expect(data.mimetype).to.be.undefined;
const initialData = {
_id: 'file_id',
name: 'filename',
filename: 'randomname',
path: '/uploads/randomname',
size: 1024,
mimetype: 'image/png'
};
file = new File(initialData);
data = file.data;
expect(data._id).to.equal(initialData._id);
expect(data.name).to.equal(initialData.name);
expect(data.filename).to.equal(initialData.filename);
expect(data.path).to.equal(initialData.path);
expect(data.size).to.equal(initialData.size);
expect(data.mimetype).to.equal(initialData.mimetype);
const update = {
_id: '12345',
name: 'updated filename',
filename: 'updated randomname',
path: '/uploads/updated randomname',
size: 2048,
mimetype: 'image/jpeg'
};
file.data = update;
data = file.data;
expect(data._id).to.equal(initialData._id);
expect(data.name).to.equal(update.name);
expect(data.filename).to.equal(update.filename);
expect(data.path).to.equal(update.path);
expect(data.size).to.equal(update.size);
expect(data.mimetype).to.equal(update.mimetype);
});
it('Saving, updating and deleting model in the database', async () => {
const initialData = {
name: 'filename',
filename: 'randomname',
path: '/uploads/randomname',
size: 1024,
mimetype: 'image/png'
};
const file = new File(initialData);
const savedFile = await file.save();
expect(savedFile._id).not.be.undefined;
expect(savedFile.name).to.equal(initialData.name);
expect(savedFile.filename).to.equal(initialData.filename);
expect(savedFile.path).to.equal(initialData.path);
expect(savedFile.size).to.equal(initialData.size);
expect(savedFile.mimetype).to.equal(initialData.mimetype);
const insertedFile = await files.findOne({ _id: file._id });
expect(insertedFile._id).to.equal(file._id);
expect(insertedFile.name).to.equal(file.name);
expect(insertedFile.filename).to.equal(file.filename);
expect(insertedFile.path).to.equal(file.path);
expect(insertedFile.size).to.equal(file.size);
expect(insertedFile.mimetype).to.equal(file.mimetype);
const updateData = {
_id: '12345',
name: 'updated filename',
filename: 'updated randomname',
path: '/uploads/updated randomname',
size: 2048,
mimetype: 'image/jpeg'
};
file.data = updateData;
await file.save();
expect(file._id).to.equal(insertedFile._id);
const updatedFile = await files.findOne({ _id: file._id });
expect(updatedFile._id).to.equal(savedFile._id);
expect(updatedFile.name).to.equal(updateData.name);
expect(updatedFile.filename).to.equal(updateData.filename);
expect(updatedFile.path).to.equal(updateData.path);
expect(updatedFile.size).to.equal(updateData.size);
expect(updatedFile.mimetype).to.equal(updateData.mimetype);
await file.destroy();
expect(file._id).to.be.undefined;
const removedFile = await files.findOne({ _id: updatedFile._id });
expect(removedFile).to.be.null;
});
it('Static get method', async () => {
const initialData = {
name: 'filename',
filename: 'randomname',
path: '/uploads/randomname',
size: 1024,
mimetype: 'image/png'
};
const file = new File(initialData);
const savedFile = await file.save();
if (savedFile._id !== undefined){
const foundFile = await File.get(savedFile._id);
const { data } = foundFile;
expect(data._id).to.equal(savedFile._id);
expect(data.name).to.equal(savedFile.name);
expect(data.filename).to.equal(savedFile.filename);
expect(data.path).to.equal(savedFile.path);
expect(data.size).to.equal(savedFile.size);
expect(data.mimetype).to.equal(savedFile.mimetype);
}
await file.destroy();
});
it('Static getByFilename method', async () => {
const initialData = {
name: 'filename',
filename: 'randomname',
path: '/uploads/randomname',
size: 1024,
mimetype: 'image/png'
};
const file = new File(initialData);
const savedFile = await file.save();
if (savedFile.filename !== undefined){
const foundFile = await File.getByFilename(savedFile.filename);
const { data } = foundFile;
expect(data._id).to.equal(savedFile._id);
expect(data.name).to.equal(savedFile.name);
expect(data.filename).to.equal(savedFile.filename);
expect(data.path).to.equal(savedFile.path);
expect(data.size).to.equal(savedFile.size);
expect(data.mimetype).to.equal(savedFile.mimetype);
}
await file.destroy();
});
it('Static getAll method', async () => {
const filesToSave = [
new File({
name: 'filename1',
filename: 'randomname1',
path: '/uploads/randomname1',
size: 1024,
mimetype: 'image/png'
}),
new File({
name: 'filename2',
filename: 'randomname2',
path: '/uploads/randomname2',
size: 2048,
mimetype: 'image/jpeg'
}),
];
const savedFiles = await Promise.all(filesToSave.map(file => file.save()));
const foundFiles = await File.getAll({ _id: { $in: savedFiles.map(file => file._id) } });
expect(foundFiles.length).to.equal(2);
foundFiles.forEach((file, i) => {
expect(file.name).to.equal(filesToSave[i].name);
expect(file.filename).to.equal(filesToSave[i].filename);
expect(file.path).to.equal(filesToSave[i].path);
expect(file.size).to.equal(filesToSave[i].size);
expect(file.mimetype).to.equal(filesToSave[i].mimetype);
});
});
});

394
src/test/models/page.ts Normal file
View file

@ -0,0 +1,394 @@
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';
import config from 'config';
import Page from '../../backend/models/page';
import translateString from '../../backend/utils/translation';
import database from '../../backend/utils/database';
const pages = database['pages'];
describe('Page model', () => {
const transformToUri = (text: string): string => {
return translateString(text
.replace(/&nbsp;/g, ' ')
.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ')
.replace(/ +/g, ' ')
.trim()
.toLowerCase()
.split(' ')
.join('-'));
};
after(() => {
const pathToDB = path.resolve(__dirname, '../../../', config.get('database'), './pages.db');
if (fs.existsSync(pathToDB)) {
fs.unlinkSync(pathToDB);
}
});
it('Working with empty model', async () => {
let page = new Page();
expect(page.data).to.be.a('object');
let {data} = page;
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.equal('0');
page = new Page();
data = page.data;
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.equal('0');
const initialData = {
_id: 'page_id',
body: {
blocks: [
{
type: 'header',
data: {
text: 'Page header'
}
}
]
}
};
page = new Page(initialData);
const json = page.toJSON();
data = page.data;
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.equal('0');
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.equal('0');
const update = {
_id: '12345',
body: {
blocks: [
{
type: 'header',
data: {
text: 'Updated page header'
}
}
]
}
};
page.data = update;
data = page.data;
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.equal('0');
});
it('Saving, updating and deleting model in the database', async () => {
const initialData = {
body: {
blocks: [
{
type: 'header',
data: {
text: 'New page header'
}
}
]
}
};
const page = new Page(initialData);
const savedPage = await page.save();
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;
const insertedPage = await pages.findOne({_id: page._id});
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 = {
body: {
blocks: [
{
type: 'header',
data: {
text: 'Updated page header'
}
}
]
},
uri: 'updated-uri'
};
page.data = updateData;
await page.save();
expect(page._id).to.equal(insertedPage._id);
const updatedPage = await pages.findOne({_id: page._id});
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();
expect(page._id).to.be.undefined;
const removedPage = await pages.findOne({_id: updatedPage._id});
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);
const 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);
const thirdSavedPage = await thirdPage.save();
expect(thirdSavedPage.uri).to.equal(transformToUri(initialData.body.blocks[0].data.text));
});
it('Static get method', async () => {
const initialData = {
body: {
blocks: [
{
type: 'header',
data: {
text: 'Test Page header'
}
}
]
}
};
const page = new Page(initialData);
const savedPage = await page.save();
if (savedPage._id == undefined) {
await page.destroy();
return;
}
const foundPage = await Page.get(savedPage._id);
const {data} = foundPage;
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();
});
it('Static getAll method', async () => {
const pagesToSave = [
new Page({
body: {
blocks: [
{
type: 'header',
data: {
text: 'Page 1 header'
}
}
]
}
}),
new Page({
body: {
blocks: [
{
type: 'header',
data: {
text: 'Page 2 header'
}
}
]
}
})
];
const savedPages = await Promise.all(pagesToSave.map(page => page.save()));
const foundPages = await Page.getAll({_id: {$in: savedPages.map(page => page._id)}});
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);
});
});
it('Parent pages', async () => {
const parent = new Page(
{
body: {
blocks: [
{
type: 'header',
data: {
text: 'Parent page header'
}
}
]
}
}
);
const {_id: parentId} = await parent.save();
const child = new Page(
{
body: {
blocks: [
{
type: 'header',
data: {
text: 'Child page header'
}
}
]
},
parent: parentId,
}
);
const {_id: childId} = await child.save();
const testedParent = await child.getParent();
expect(testedParent).to.be.not.null;
if (testedParent) {
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;
expect(children.length).to.equal(1);
const temp: Page|undefined = children.pop();
const testedChild: Page = !temp ? new Page({}) : temp;
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);
parent.destroy();
child.destroy();
});
it('Extracting title from page body', async () => {
const pageData = {
body: {
blocks: [
{
type: 'header',
data: {
text: 'Page header'
}
}
]
}
};
const page = new Page(pageData);
expect(page.title).to.equal(pageData.body.blocks[0].data.text);
});
it('test deletion', async () => {
const pageIndexes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const orders = {
'0' : ['1', '2', '3'],
'1' : ['4', '5'],
'5' : ['6', '7', '8'],
'3' : ['9'],
} as { [key: string]: string[] };
function deleteRecursively(startFrom: string): void {
const order: string[] = orders[startFrom];
if (!order) {
const found = pageIndexes.indexOf(startFrom);
pageIndexes.splice(found, 1);
return;
}
order.forEach(id => {
deleteRecursively(id);
});
const found = pageIndexes.indexOf(startFrom);
pageIndexes.splice(found, 1);
}
});
});

View file

@ -0,0 +1,149 @@
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';
import config from 'config';
import PageOrder from '../../backend/models/pageOrder';
import database from '../../backend/utils/database';
const pagesOrder = database['pagesOrder'];
describe('PageOrder model', () => {
after(() => {
const pathToDB = path.resolve(__dirname, '../../../', config.get('database'), './pagesOrder.db');
if (fs.existsSync(pathToDB)) {
fs.unlinkSync(pathToDB);
}
});
it('Empty Model', async () => {
const pageOrder = new PageOrder();
expect(pageOrder.data).to.be.a('object');
let {data} = pageOrder;
expect(data._id).to.be.undefined;
expect(data.page).to.be.to.equal('0');
expect(data.order).to.be.an('array').that.is.empty;
let page = new PageOrder();
data = page.data;
expect(data._id).to.be.undefined;
expect(data.page).to.be.to.equal('0');
expect(data.order).to.be.an('array').that.is.empty;
const testData = {
_id: 'order_id',
page: 'page_id',
order: []
};
page = new PageOrder(testData);
data = page.data;
expect(data._id).to.equal(testData._id);
expect(data.page).to.equal(testData.page);
expect(data.order).to.be.an('array').that.is.empty;
});
it('Testing Model methods', async () => {
const testData = {
page: 'page_id',
order: ['1', '2']
};
const pageOrder = new PageOrder(testData);
const {data} = await pageOrder.save();
expect(data._id).not.be.undefined;
expect(data.page).to.equal(testData.page);
expect(data.order).to.deep.equals(testData.order);
const insertedPageOrder = await pagesOrder.findOne({_id: data._id}) as PageOrder;
expect(insertedPageOrder._id).to.equal(data._id);
expect(insertedPageOrder.page).to.equal(data.page);
expect(insertedPageOrder.order).to.deep.equal(data.order);
const updateData = {
page: 'page_id_2',
order: ['3']
};
pageOrder.data = updateData;
await pageOrder.save();
expect(pageOrder.data._id).to.equal(insertedPageOrder._id);
const updatedData = await pagesOrder.findOne({_id: insertedPageOrder._id}) as PageOrder;
expect(updatedData.page).to.equal(updateData.page);
expect(updatedData.order).to.deep.equal(updateData.order);
await pageOrder.destroy();
expect(pageOrder.data._id).to.be.undefined;
const removedPage = await pagesOrder.findOne({_id: updatedData._id});
expect(removedPage).to.be.null;
});
it('Testing push and remove order methods', async () => {
const testData = {
page: 'page_id',
order: ['1', '2']
};
const pageOrder = new PageOrder(testData);
await pageOrder.save();
pageOrder.push('3');
expect(pageOrder.data.order).to.be.an('array').that.is.not.empty;
if (pageOrder.data.order !== undefined) {
pageOrder.data.order.forEach((el) => {
expect(el).to.be.an('string');
});
}
expect(pageOrder.data.order).to.deep.equals(['1', '2', '3']);
pageOrder.remove('2');
expect(pageOrder.data.order).to.deep.equals(['1', '3']);
expect(() => {
pageOrder.push(3);
}).to.throw('given id is not string');
pageOrder.push('4');
pageOrder.push('5');
pageOrder.push('2');
pageOrder.putAbove('2', '3');
expect(pageOrder.data.order).to.deep.equals(['1', '2', '3', '4', '5']);
pageOrder.putAbove('2', '10');
expect(pageOrder.data.order).to.deep.equals(['1', '2', '3', '4', '5']);
await pageOrder.destroy();
});
it('Testing static methods', async () => {
const testData = {
page: 'page_id',
order: ['1', '2']
};
const pageOrder = new PageOrder(testData);
const insertedData = await pageOrder.save();
if (insertedData.data.page !== undefined) {
const insertedPageOrder = await PageOrder.get(insertedData.data.page);
expect(insertedPageOrder).to.instanceOf(PageOrder);
expect(insertedPageOrder.data._id).to.be.equal(insertedData.data._id);
}
const emptyInstance = await PageOrder.get('');
expect(emptyInstance.data.page).to.be.equal('0');
expect(emptyInstance.data.order).to.be.an('array').that.is.empty;
await pageOrder.destroy();
});
});