Feat/frontend vitest (#6469)

* Added vitest tests.

* Added Settings tests to vitest - not working

* Added attributes and attributemap to vitest.

* Added more tests.

* Also run the vitest tests.

* Also run withoutPlugins

* Fixed pnpm lock
This commit is contained in:
SamTV12345 2024-08-16 22:55:42 +02:00 committed by GitHub
parent babfaab4df
commit c7a2dea4d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1092 additions and 552 deletions

View file

@ -1,8 +1,9 @@
'use strict';
const AttributeMap = require('../../../static/js/AttributeMap');
const AttributeMap = require('../../../static/js/AttributeMap.js');
const AttributePool = require('../../../static/js/AttributePool');
const attributes = require('../../../static/js/attributes');
import {expect, describe, it, beforeEach} from 'vitest'
describe('AttributeMap', function () {
const attribs = [
@ -10,7 +11,7 @@ describe('AttributeMap', function () {
['baz', 'bif'],
['emptyValue', ''],
];
let pool;
let pool: { eachAttrib: (arg0: () => number) => void; putAttrib: (arg0: string[]) => any; getAttrib: (arg0: number) => any; };
const getPoolSize = () => {
let n = 0;
@ -66,7 +67,7 @@ describe('AttributeMap', function () {
['number', 1, '1'],
];
for (const [desc, input, want] of testCases) {
describe(desc, function () {
describe(desc as string, function () {
it('key is coerced to string', async function () {
const m = new AttributeMap(pool);
m.set(input, 'value');
@ -116,8 +117,9 @@ describe('AttributeMap', function () {
});
for (const funcName of ['update', 'updateFromString']) {
const callUpdateFn = (m, ...args) => {
const callUpdateFn = (m: any, ...args: (boolean | (string | null | undefined)[][])[]) => {
if (funcName === 'updateFromString') {
// @ts-ignore
args[0] = attributes.attribsToString(attributes.sort([...args[0]]), pool);
}
return AttributeMap.prototype[funcName].call(m, ...args);

View file

@ -1,11 +1,15 @@
'use strict';
import {APool} from "../../../node/types/PadType";
const AttributePool = require('../../../static/js/AttributePool');
const attributes = require('../../../static/js/attributes');
import {expect, describe, it, beforeEach} from 'vitest';
describe('attributes', function () {
const attribs = [['foo', 'bar'], ['baz', 'bif']];
let pool;
let pool: APool;
beforeEach(async function () {
pool = new AttributePool();
@ -14,7 +18,7 @@ describe('attributes', function () {
describe('decodeAttribString', function () {
it('is a generator function', async function () {
expect(attributes.decodeAttribString).to.be.a((function* () {}).constructor);
expect(attributes.decodeAttribString.constructor.name).to.equal('GeneratorFunction');
});
describe('rejects invalid attribute strings', function () {
@ -22,7 +26,7 @@ describe('attributes', function () {
for (const tc of testCases) {
it(JSON.stringify(tc), async function () {
expect(() => [...attributes.decodeAttribString(tc)])
.to.throwException(/invalid character/);
.toThrowError(/invalid character/);
});
}
});
@ -56,7 +60,7 @@ describe('attributes', function () {
['set', new Set([0, 1])],
];
for (const [desc, input] of testCases) {
it(desc, async function () {
it(desc as string, async function () {
expect(attributes.encodeAttribString(input)).to.equal('*0*1');
});
}
@ -74,7 +78,7 @@ describe('attributes', function () {
];
for (const [input, wantErr] of testCases) {
it(JSON.stringify(input), async function () {
expect(() => attributes.encodeAttribString(input)).to.throwException(wantErr);
expect(() => attributes.encodeAttribString(input)).toThrowError(wantErr as RegExp);
});
}
});
@ -101,7 +105,7 @@ describe('attributes', function () {
describe('attribsFromNums', function () {
it('is a generator function', async function () {
expect(attributes.attribsFromNums).to.be.a((function* () {}).constructor);
expect(attributes.attribsFromNums.constructor.name).to.equal("GeneratorFunction");
});
describe('accepts any kind of iterable', function () {
@ -112,7 +116,7 @@ describe('attributes', function () {
];
for (const [desc, input] of testCases) {
it(desc, async function () {
it(desc as string, async function () {
const gotAttribs = [...attributes.attribsFromNums(input, pool)];
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(attribs));
});
@ -132,7 +136,7 @@ describe('attributes', function () {
];
for (const [input, wantErr] of testCases) {
it(JSON.stringify(input), async function () {
expect(() => [...attributes.attribsFromNums(input, pool)]).to.throwException(wantErr);
expect(() => [...attributes.attribsFromNums(input, pool)]).toThrowError(wantErr as RegExp);
});
}
});
@ -156,7 +160,7 @@ describe('attributes', function () {
describe('attribsToNums', function () {
it('is a generator function', async function () {
expect(attributes.attribsToNums).to.be.a((function* () {}).constructor);
expect(attributes.attribsToNums.constructor.name).to.equal("GeneratorFunction")
});
describe('accepts any kind of iterable', function () {
@ -167,7 +171,7 @@ describe('attributes', function () {
];
for (const [desc, input] of testCases) {
it(desc, async function () {
it(desc as string, async function () {
const gotNums = [...attributes.attribsToNums(input, pool)];
expect(JSON.stringify(gotNums)).to.equal(JSON.stringify([0, 1]));
});
@ -178,7 +182,7 @@ describe('attributes', function () {
const testCases = [null, [null]];
for (const input of testCases) {
it(JSON.stringify(input), async function () {
expect(() => [...attributes.attribsToNums(input, pool)]).to.throwException();
expect(() => [...attributes.attribsToNums(input, pool)]).toThrowError();
});
}
});
@ -224,12 +228,12 @@ describe('attributes', function () {
['number', 1, '1'],
];
for (const [desc, inputVal, wantVal] of testCases) {
describe(desc, function () {
describe(desc as string, function () {
for (const [desc, inputAttribs, wantAttribs] of [
['key is coerced to string', [[inputVal, 'value']], [[wantVal, 'value']]],
['value is coerced to string', [['key', inputVal]], [['key', wantVal]]],
]) {
it(desc, async function () {
it(desc as string, async function () {
const gotNums = [...attributes.attribsToNums(inputAttribs, pool)];
// Each attrib in inputAttribs is expected to be new to the pool.
const wantNums = [...Array(attribs.length + 1).keys()].slice(attribs.length);
@ -245,7 +249,7 @@ describe('attributes', function () {
describe('attribsFromString', function () {
it('is a generator function', async function () {
expect(attributes.attribsFromString).to.be.a((function* () {}).constructor);
expect(attributes.attribsFromString.constructor.name).to.equal('GeneratorFunction');
});
describe('rejects invalid attribute strings', function () {
@ -261,7 +265,7 @@ describe('attributes', function () {
];
for (const [input, wantErr] of testCases) {
it(JSON.stringify(input), async function () {
expect(() => [...attributes.attribsFromString(input, pool)]).to.throwException(wantErr);
expect(() => [...attributes.attribsFromString(input, pool)]).toThrowError(wantErr);
});
}
});
@ -292,7 +296,7 @@ describe('attributes', function () {
];
for (const [desc, input] of testCases) {
it(desc, async function () {
it(desc as string, async function () {
const got = attributes.attribsToString(input, pool);
expect(got).to.equal('*0*1');
});
@ -303,7 +307,7 @@ describe('attributes', function () {
const testCases = [null, [null]];
for (const input of testCases) {
it(JSON.stringify(input), async function () {
expect(() => attributes.attribsToString(input, pool)).to.throwException();
expect(() => attributes.attribsToString(input, pool)).toThrowError();
});
}
});

View file

@ -0,0 +1,398 @@
'use strict';
/*
* While importexport tests target the `setHTML` API endpoint, which is nearly identical to what
* happens when a user manually imports a document via the UI, the contentcollector tests here don't
* use rehype to process the document. Rehype removes spaces and newĺines were applicable, so the
* expected results here can differ from importexport.js.
*
* If you add tests here, please also add them to importexport.js
*/
import {APool} from "../../../node/types/PadType";
const AttributePool = require('../../../static/js/AttributePool');
const Changeset = require('../../../static/js/Changeset');
const assert = require('assert').strict;
const attributes = require('../../../static/js/attributes');
const contentcollector = require('../../../static/js/contentcollector');
const jsdom = require('jsdom');
import {describe, it, beforeAll, test} from 'vitest';
// All test case `wantAlines` values must only refer to attributes in this list so that the
// attribute numbers do not change due to changes in pool insertion order.
const knownAttribs = [
['insertorder', 'first'],
['italic', 'true'],
['list', 'bullet1'],
['list', 'bullet2'],
['list', 'number1'],
['list', 'number2'],
['lmkr', '1'],
['start', '1'],
['start', '2'],
];
const testCases = [
{
description: 'Simple',
html: '<html><body><p>foo</p></body></html>',
wantAlines: ['+3'],
wantText: ['foo'],
},
{
description: 'Line starts with asterisk',
html: '<html><body><p>*foo</p></body></html>',
wantAlines: ['+4'],
wantText: ['*foo'],
},
{
description: 'Complex nested Li',
html: '<!doctype html><html><body><ol><li>one</li><li><ol><li>1.1</li></ol></li><li>two</li></ol></body></html>',
wantAlines: [
'*0*4*6*7+1+3',
'*0*5*6*8+1+3',
'*0*4*6*8+1+3',
],
wantText: [
'*one', '*1.1', '*two',
],
},
{
description: 'Complex list of different types',
html: '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two</li><li>0</li><li>1</li><li>2<ul class="bullet"><li>3</li><li>4</li></ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</li></ol></li></ol></body></html>',
wantAlines: [
'*0*2*6+1+3',
'*0*2*6+1+3',
'*0*2*6+1+1',
'*0*2*6+1+1',
'*0*2*6+1+1',
'*0*3*6+1+1',
'*0*3*6+1+1',
'*0*4*6*7+1+4',
'*0*5*6*8+1+5',
'*0*5*6*8+1+5',
],
wantText: [
'*one',
'*two',
'*0',
'*1',
'*2',
'*3',
'*4',
'*item',
'*item1',
'*item2',
],
},
{
description: 'Tests if uls properly get attributes',
html: '<html><body><ul><li>a</li><li>b</li></ul><div>div</div><p>foo</p></body></html>',
wantAlines: [
'*0*2*6+1+1',
'*0*2*6+1+1',
'+3',
'+3',
],
wantText: ['*a', '*b', 'div', 'foo'],
},
{
description: 'Tests if indented uls properly get attributes',
html: '<html><body><ul><li>a</li><ul><li>b</li></ul><li>a</li></ul><p>foo</p></body></html>',
wantAlines: [
'*0*2*6+1+1',
'*0*3*6+1+1',
'*0*2*6+1+1',
'+3',
],
wantText: ['*a', '*b', '*a', 'foo'],
},
{
description: 'Tests if ols properly get line numbers when in a normal OL',
html: '<html><body><ol><li>a</li><li>b</li><li>c</li></ol><p>test</p></body></html>',
wantAlines: [
'*0*4*6*7+1+1',
'*0*4*6*7+1+1',
'*0*4*6*7+1+1',
'+4',
],
wantText: ['*a', '*b', '*c', 'test'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
},
{
description: 'A single completely empty line break within an ol should reset count if OL is closed off..',
html: '<html><body><ol><li>should be 1</li></ol><p>hello</p><ol><li>should be 1</li><li>should be 2</li></ol><p></p></body></html>',
wantAlines: [
'*0*4*6*7+1+b',
'+5',
'*0*4*6*8+1+b',
'*0*4*6*8+1+b',
'',
],
wantText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''],
noteToSelf: "Shouldn't include attribute marker in the <p> line",
},
{
description: 'A single <p></p> should create a new line',
html: '<html><body><p></p><p></p></body></html>',
wantAlines: ['', ''],
wantText: ['', ''],
noteToSelf: '<p></p>should create a line break but not break numbering',
},
{
description: 'Tests if ols properly get line numbers when in a normal OL #2',
html: '<html><body>a<ol><li>b<ol><li>c</li></ol></ol>notlist<p>foo</p></body></html>',
wantAlines: [
'+1',
'*0*4*6*7+1+1',
'*0*5*6*8+1+1',
'+7',
'+3',
],
wantText: ['a', '*b', '*c', 'notlist', 'foo'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
},
{
description: 'First item being an UL then subsequent being OL will fail',
html: '<html><body><ul><li>a<ol><li>b</li><li>c</li></ol></li></ul></body></html>',
wantAlines: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'],
wantText: ['a', '*b', '*c'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
disabled: true,
},
{
description: 'A single completely empty line break within an ol should NOT reset count',
html: '<html><body><ol><li>should be 1</li><p></p><li>should be 2</li><li>should be 3</li></ol><p></p></body></html>',
wantAlines: [],
wantText: ['*should be 1', '*should be 2', '*should be 3'],
noteToSelf: "<p></p>should create a line break but not break numbering -- This is what I can't get working!",
disabled: true,
},
{
description: 'Content outside body should be ignored',
html: '<html><head><title>title</title><style></style></head><body>empty<br></body></html>',
wantAlines: ['+5'],
wantText: ['empty'],
},
{
description: 'Multiple spaces should be preserved',
html: '<html><body>Text with more than one space.<br></body></html>',
wantAlines: ['+10'],
wantText: ['Text with more than one space.'],
},
{
description: 'non-breaking and normal space should be preserved',
html: '<html><body>Text&nbsp;with&nbsp; more&nbsp;&nbsp;&nbsp;than &nbsp;one space.<br></body></html>',
wantAlines: ['+10'],
wantText: ['Text with more than one space.'],
},
{
description: 'Multiple nbsp should be preserved',
html: '<html><body>&nbsp;&nbsp;<br></body></html>',
wantAlines: ['+2'],
wantText: [' '],
},
{
description: 'Multiple nbsp between words ',
html: '<html><body>&nbsp;&nbsp;word1&nbsp;&nbsp;word2&nbsp;&nbsp;&nbsp;word3<br></body></html>',
wantAlines: ['+m'],
wantText: [' word1 word2 word3'],
},
{
description: 'A non-breaking space preceded by a normal space',
html: '<html><body> &nbsp;word1 &nbsp;word2 &nbsp;word3<br></body></html>',
wantAlines: ['+l'],
wantText: [' word1 word2 word3'],
},
{
description: 'A non-breaking space followed by a normal space',
html: '<html><body>&nbsp; word1&nbsp; word2&nbsp; word3<br></body></html>',
wantAlines: ['+l'],
wantText: [' word1 word2 word3'],
},
{
description: 'Don\'t collapse spaces that follow a newline',
html: '<!doctype html><html><body>something<br> something<br></body></html>',
wantAlines: ['+9', '+m'],
wantText: ['something', ' something'],
},
{
description: 'Don\'t collapse spaces that follow a empty paragraph',
html: '<!doctype html><html><body>something<p></p> something<br></body></html>',
wantAlines: ['+9', '', '+m'],
wantText: ['something', '', ' something'],
},
{
description: 'Don\'t collapse spaces that preceed/follow a newline',
html: '<html><body>something <br> something<br></body></html>',
wantAlines: ['+l', '+m'],
wantText: ['something ', ' something'],
},
{
description: 'Don\'t collapse spaces that preceed/follow a empty paragraph',
html: '<html><body>something <p></p> something<br></body></html>',
wantAlines: ['+l', '', '+m'],
wantText: ['something ', '', ' something'],
},
{
description: 'Don\'t collapse non-breaking spaces that follow a newline',
html: '<html><body>something<br>&nbsp;&nbsp;&nbsp;something<br></body></html>',
wantAlines: ['+9', '+c'],
wantText: ['something', ' something'],
},
{
description: 'Don\'t collapse non-breaking spaces that follow a paragraph',
html: '<html><body>something<p></p>&nbsp;&nbsp;&nbsp;something<br></body></html>',
wantAlines: ['+9', '', '+c'],
wantText: ['something', '', ' something'],
},
{
description: 'Preserve all spaces when multiple are present',
html: '<html><body>Need <span> more </span> space<i> s </i> !<br></body></html>',
wantAlines: ['+h*1+4+2'],
wantText: ['Need more space s !'],
},
{
description: 'Newlines and multiple spaces across newlines should be preserved',
html: `
<html><body>Need
<span> more </span>
space
<i> s </i>
!<br></body></html>`,
wantAlines: ['+19*1+4+b'],
wantText: ['Need more space s !'],
},
{
description: 'Multiple new lines at the beginning should be preserved',
html: '<html><body><br><br><p></p><p></p>first line<br><br>second line<br></body></html>',
wantAlines: ['', '', '', '', '+a', '', '+b'],
wantText: ['', '', '', '', 'first line', '', 'second line'],
},
{
description: 'A paragraph with multiple lines should not loose spaces when lines are combined',
html: `<html><body><p>
а б в г ґ д е є ж з и і ї й к л м н о
п р с т у ф х ц ч ш щ ю я ь</p>
</body></html>`,
wantAlines: ['+1t'],
wantText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'],
},
{
description: 'lines in preformatted text should be kept intact',
html: `<html><body><p>
а б в г ґ д е є ж з и і ї й к л м н о</p><pre>multiple
lines
in
pre
</pre><p>п р с т у ф х ц ч ш щ ю я
ь</p>
</body></html>`,
wantAlines: ['+11', '+8', '+5', '+2', '+3', '+r'],
wantText: [
'а б в г ґ д е є ж з и і ї й к л м н о',
'multiple',
'lines',
'in',
'pre',
'п р с т у ф х ц ч ш щ ю я ь',
],
},
{
description: 'pre should be on a new line not preceded by a space',
html: `<html><body><p>
1
</p><pre>preline
</pre></body></html>`,
wantAlines: ['+6', '+7'],
wantText: [' 1 ', 'preline'],
},
{
description: 'Preserve spaces on the beginning and end of a element',
html: '<html><body>Need<span> more </span>space<i> s </i>!<br></body></html>',
wantAlines: ['+f*1+3+1'],
wantText: ['Need more space s !'],
},
{
description: 'Preserve spaces outside elements',
html: '<html><body>Need <span>more</span> space <i>s</i> !<br></body></html>',
wantAlines: ['+g*1+1+2'],
wantText: ['Need more space s !'],
},
{
description: 'Preserve spaces at the end of an element',
html: '<html><body>Need <span>more </span>space <i>s </i>!<br></body></html>',
wantAlines: ['+g*1+2+1'],
wantText: ['Need more space s !'],
},
{
description: 'Preserve spaces at the start of an element',
html: '<html><body>Need<span> more</span> space<i> s</i> !<br></body></html>',
wantAlines: ['+f*1+2+2'],
wantText: ['Need more space s !'],
},
];
describe(__filename, function () {
for (const tc of testCases) {
describe(tc.description, function () {
let apool: APool;
let result: {
lines: string[],
lineAttribs: string[],
};
if (tc.disabled) {
test.skip('If disabled we do not run the test');
return;
}
beforeAll(async function () {
const {window: {document}} = new jsdom.JSDOM(tc.html);
apool = new AttributePool();
// To reduce test fragility, the attribute pool is seeded with `knownAttribs`, and all
// attributes in `tc.wantAlines` must be in `knownAttribs`. (This guarantees that attribute
// numbers do not change if the attribute processing code changes.)
for (const attrib of knownAttribs) apool.putAttrib(attrib);
for (const aline of tc.wantAlines) {
for (const op of Changeset.deserializeOps(aline)) {
for (const n of attributes.decodeAttribString(op.attribs)) {
assert(n < knownAttribs.length);
}
}
}
const cc = contentcollector.makeContentCollector(true, null, apool);
cc.collectContent(document.body);
result = cc.finish();
console.log(result);
});
it('text matches', async function () {
assert.deepEqual(result.lines, tc.wantText);
});
it('alines match', async function () {
assert.deepEqual(result.lineAttribs, tc.wantAlines);
});
it('attributes are sorted in canonical order', async function () {
const gotAttribs:string[][][] = [];
const wantAttribs = [];
for (const aline of result.lineAttribs) {
const gotAlineAttribs:string[][] = [];
gotAttribs.push(gotAlineAttribs);
const wantAlineAttribs:string[] = [];
wantAttribs.push(wantAlineAttribs);
for (const op of Changeset.deserializeOps(aline)) {
const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)];
gotAlineAttribs.push(gotOpAttribs);
wantAlineAttribs.push(attributes.sort([...gotOpAttribs]));
}
}
assert.deepEqual(gotAttribs, wantAttribs);
});
});
}
});

View file

@ -1,16 +1,13 @@
'use strict';
import {MapArrayType} from "../../../node/types/MapType";
import {strict as assert} from "assert";
const {padutils} = require('../../../static/js/pad_utils');
import {describe, it, expect, afterEach, beforeAll} from "vitest";
describe(__filename, function () {
describe('warnDeprecated', function () {
const {warnDeprecated} = padutils;
const backups:MapArrayType<any> = {};
before(async function () {
beforeAll(async function () {
backups.logger = warnDeprecated.logger;
});
@ -36,10 +33,10 @@ describe(__filename, function () {
for (const [now, want] of testCases) { // In a loop so that the stack trace is the same.
warnDeprecated._rl.now = () => now;
warnDeprecated();
assert.equal(got, want);
expect(got).toEqual(want);
}
warnDeprecated(); // Should have a different stack trace.
assert.equal(got, testCases[testCases.length - 1][1] + 1);
expect(got).toEqual(testCases[testCases.length - 1][1] + 1);
});
});
});

View file

@ -1,8 +1,7 @@
'use strict';
import {strict as assert} from "assert";
import path from 'path';
const sanitizePathname = require('../../../node/utils/sanitizePathname');
import sanitizePathname from '../../../node/utils/sanitizePathname';
import {describe, it, expect} from 'vitest';
describe(__filename, function () {
describe('absolute paths rejected', function () {
@ -21,7 +20,7 @@ describe(__filename, function () {
for (const [platform, p] of testCases) {
it(`${platform} ${p}`, async function () {
// @ts-ignore
assert.throws(() => sanitizePathname(p, path[platform]), {message: /absolute path/});
expect(() => sanitizePathname(p, path[platform] as any)).toThrowError(/absolute path/);
});
}
});

View file

@ -0,0 +1,55 @@
'use strict';
const SkipList = require('ep_etherpad-lite/static/js/skiplist');
import {expect, describe, it} from 'vitest';
describe('skiplist.js', function () {
it('rejects null keys', async function () {
const skiplist = new SkipList();
for (const key of [undefined, null]) {
expect(() => skiplist.push({key})).toThrowError();
}
});
it('rejects duplicate keys', async function () {
const skiplist = new SkipList();
skiplist.push({key: 'foo'});
expect(() => skiplist.push({key: 'foo'})).toThrowError();
});
it('atOffset() returns last entry that touches offset', async function () {
const skiplist = new SkipList();
const entries: { key: string; width: number; }[] = [];
let nextId = 0;
const makeEntry = (width: number) => {
const entry = {key: `id${nextId++}`, width};
entries.push(entry);
return entry;
};
skiplist.push(makeEntry(5));
expect(skiplist.atOffset(4)).toBe(entries[0]);
expect(skiplist.atOffset(5)).toBe(entries[0]);
expect(() => skiplist.atOffset(6)).toThrowError();
skiplist.push(makeEntry(0));
expect(skiplist.atOffset(4)).toBe(entries[0]);
expect(skiplist.atOffset(5)).toBe(entries[1]);
expect(() => skiplist.atOffset(6)).toThrowError();
skiplist.push(makeEntry(0));
expect(skiplist.atOffset(4)).toBe(entries[0]);
expect(skiplist.atOffset(5)).toBe(entries[2]);
expect(() => skiplist.atOffset(6)).toThrowError();
skiplist.splice(2, 0, [makeEntry(0)]);
expect(skiplist.atOffset(4)).toBe(entries[0]);
expect(skiplist.atOffset(5)).toBe(entries[2]);
expect(() => skiplist.atOffset(6)).toThrowError();
skiplist.push(makeEntry(3));
expect(skiplist.atOffset(4)).toBe(entries[0]);
expect(skiplist.atOffset(5)).toBe(entries[4]);
expect(skiplist.atOffset(6)).toBe(entries[4]);
});
});

View file

@ -40,7 +40,7 @@ describe(__filename, function () {
it('do nothing', async function () {
await agent.get('/p/UPPERCASEpad')
.expect(200);
.expect(200);
});
});
@ -50,8 +50,8 @@ describe(__filename, function () {
});
it('lowercase pad ids', async function () {
await agent.get('/p/UPPERCASEpad')
.expect(302)
.expect('location', 'uppercasepad');
.expect(302)
.expect('location', 'uppercasepad');
});
it('keeps old pads accessible', async function () {

View file

@ -6,87 +6,87 @@ import path from 'path';
import process from 'process';
describe(__filename, function () {
describe('parseSettings', function () {
let settings: any;
const envVarSubstTestCases = [
{name: 'true', val: 'true', var: 'SET_VAR_TRUE', want: true},
{name: 'false', val: 'false', var: 'SET_VAR_FALSE', want: false},
{name: 'null', val: 'null', var: 'SET_VAR_NULL', want: null},
{name: 'undefined', val: 'undefined', var: 'SET_VAR_UNDEFINED', want: undefined},
{name: 'number', val: '123', var: 'SET_VAR_NUMBER', want: 123},
{name: 'string', val: 'foo', var: 'SET_VAR_STRING', want: 'foo'},
{name: 'empty string', val: '', var: 'SET_VAR_EMPTY_STRING', want: ''},
];
describe('parseSettings', function () {
let settings: any;
const envVarSubstTestCases = [
{name: 'true', val: 'true', var: 'SET_VAR_TRUE', want: true},
{name: 'false', val: 'false', var: 'SET_VAR_FALSE', want: false},
{name: 'null', val: 'null', var: 'SET_VAR_NULL', want: null},
{name: 'undefined', val: 'undefined', var: 'SET_VAR_UNDEFINED', want: undefined},
{name: 'number', val: '123', var: 'SET_VAR_NUMBER', want: 123},
{name: 'string', val: 'foo', var: 'SET_VAR_STRING', want: 'foo'},
{name: 'empty string', val: '', var: 'SET_VAR_EMPTY_STRING', want: ''},
];
before(async function () {
for (const tc of envVarSubstTestCases) process.env[tc.var] = tc.val;
delete process.env.UNSET_VAR;
settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert(settings != null);
});
describe('environment variable substitution', function () {
describe('set', function () {
for (const tc of envVarSubstTestCases) {
it(tc.name, async function () {
const obj = settings['environment variable substitution'].set;
if (tc.name === 'undefined') {
assert(!(tc.name in obj));
} else {
assert.equal(obj[tc.name], tc.want);
}
});
}
});
describe('unset', function () {
it('no default', async function () {
const obj = settings['environment variable substitution'].unset;
assert.equal(obj['no default'], null);
});
for (const tc of envVarSubstTestCases) {
it(tc.name, async function () {
const obj = settings['environment variable substitution'].unset;
if (tc.name === 'undefined') {
assert(!(tc.name in obj));
} else {
assert.equal(obj[tc.name], tc.want);
}
});
}
});
});
before(async function () {
for (const tc of envVarSubstTestCases) process.env[tc.var] = tc.val;
delete process.env.UNSET_VAR;
settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert(settings != null);
});
describe('environment variable substitution', function () {
describe('set', function () {
for (const tc of envVarSubstTestCases) {
it(tc.name, async function () {
const obj = settings['environment variable substitution'].set;
if (tc.name === 'undefined') {
assert(!(tc.name in obj));
} else {
assert.equal(obj[tc.name], tc.want);
}
});
}
});
describe("Parse plugin settings", function () {
describe('unset', function () {
it('no default', async function () {
const obj = settings['environment variable substitution'].unset;
assert.equal(obj['no default'], null);
});
before(async function () {
process.env["EP__ADMIN__PASSWORD"] = "test"
})
for (const tc of envVarSubstTestCases) {
it(tc.name, async function () {
const obj = settings['environment variable substitution'].unset;
if (tc.name === 'undefined') {
assert(!(tc.name in obj));
} else {
assert.equal(obj[tc.name], tc.want);
}
});
}
});
});
});
it('should parse plugin settings', async function () {
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.equal(settings.ADMIN.PASSWORD, "test");
})
it('should bundle settings with same path', async function () {
process.env["EP__ADMIN__USERNAME"] = "test"
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.deepEqual(settings.ADMIN, {PASSWORD: "test", USERNAME: "test"});
})
describe("Parse plugin settings", function () {
it("Can set the ep themes", async function () {
process.env["EP__ep_themes__default_theme"] = "hacker"
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.deepEqual(settings.ep_themes, {"default_theme": "hacker"});
})
it("can set the ep_webrtc settings", async function () {
process.env["EP__ep_webrtc__enabled"] = "true"
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.deepEqual(settings.ep_webrtc, {"enabled": true});
})
before(async function () {
process.env["EP__ADMIN__PASSWORD"] = "test"
})
it('should parse plugin settings', async function () {
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.equal(settings.ADMIN.PASSWORD, "test");
})
it('should bundle settings with same path', async function () {
process.env["EP__ADMIN__USERNAME"] = "test"
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.deepEqual(settings.ADMIN, {PASSWORD: "test", USERNAME: "test"});
})
it("Can set the ep themes", async function () {
process.env["EP__ep_themes__default_theme"] = "hacker"
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.deepEqual(settings.ep_themes, {"default_theme": "hacker"});
})
it("can set the ep_webrtc settings", async function () {
process.env["EP__ep_webrtc__enabled"] = "true"
let settings = parseSettings(path.join(__dirname, 'settings.json'), true);
assert.deepEqual(settings.ep_webrtc, {"enabled": true});
})
})
});

View file

@ -4,11 +4,19 @@ const Changeset = require('../../../static/js/Changeset');
const {padutils} = require('../../../static/js/pad_utils');
const {poolOrArray} = require('../easysync-helper.js');
import {describe, it, expect} from 'vitest'
describe('easysync-assembler', function () {
it('opAssembler', async function () {
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
const assem = Changeset.opAssembler();
for (const op of Changeset.deserializeOps(x)) assem.append(op);
var opLength = 0
for (const op of Changeset.deserializeOps(x)){
console.log(op)
assem.append(op);
opLength++
}
expect(assem.toString()).to.equal(x);
});
@ -145,14 +153,23 @@ describe('easysync-assembler', function () {
next() { const v = this._n.value; this._n = ops.next(); return v; },
};
const assem = Changeset.smartOpAssembler();
assem.append(iter.next());
assem.append(iter.next());
assem.append(iter.next());
var iter1 = iter.next()
assem.append(iter1);
var iter2 = iter.next()
assem.append(iter2);
var iter3 = iter.next()
assem.append(iter3);
console.log(assem.toString());
assem.clear();
assem.append(iter.next());
assem.append(iter.next());
console.log(assem.toString());
assem.clear();
while (iter.hasNext()) assem.append(iter.next());
let counter = 0;
while (iter.hasNext()) {
console.log(counter++)
assem.append(iter.next());
}
assem.endDocument();
expect(assem.toString()).to.equal('-1+1*0+1=1-1+1|c=c-1');
});

View file

@ -4,6 +4,8 @@ const Changeset = require('../../../static/js/Changeset');
const AttributePool = require('../../../static/js/AttributePool');
const {randomMultiline, poolOrArray} = require('../easysync-helper.js');
const {padutils} = require('../../../static/js/pad_utils');
import {describe, it, expect} from 'vitest'
describe('easysync-other', function () {
describe('filter attribute numbers', function () {
@ -66,7 +68,8 @@ describe('easysync-other', function () {
it('testMakeSplice', async function () {
const t = 'a\nb\nc\n';
const t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, 'def'), t);
let splice = Changeset.makeSplice(t, 5, 0, 'def')
const t2 = Changeset.applyToText(splice, t);
expect(t2).to.equal('a\nb\ncdef\n');
});

View file

@ -1,54 +0,0 @@
'use strict';
const SkipList = require('ep_etherpad-lite/static/js/skiplist');
describe('skiplist.js', function () {
it('rejects null keys', async function () {
const skiplist = new SkipList();
for (const key of [undefined, null]) {
expect(() => skiplist.push({key})).to.throwError();
}
});
it('rejects duplicate keys', async function () {
const skiplist = new SkipList();
skiplist.push({key: 'foo'});
expect(() => skiplist.push({key: 'foo'})).to.throwError();
});
it('atOffset() returns last entry that touches offset', async function () {
const skiplist = new SkipList();
const entries = [];
let nextId = 0;
const makeEntry = (width) => {
const entry = {key: `id${nextId++}`, width};
entries.push(entry);
return entry;
};
skiplist.push(makeEntry(5));
expect(skiplist.atOffset(4)).to.be(entries[0]);
expect(skiplist.atOffset(5)).to.be(entries[0]);
expect(() => skiplist.atOffset(6)).to.throwError();
skiplist.push(makeEntry(0));
expect(skiplist.atOffset(4)).to.be(entries[0]);
expect(skiplist.atOffset(5)).to.be(entries[1]);
expect(() => skiplist.atOffset(6)).to.throwError();
skiplist.push(makeEntry(0));
expect(skiplist.atOffset(4)).to.be(entries[0]);
expect(skiplist.atOffset(5)).to.be(entries[2]);
expect(() => skiplist.atOffset(6)).to.throwError();
skiplist.splice(2, 0, [makeEntry(0)]);
expect(skiplist.atOffset(4)).to.be(entries[0]);
expect(skiplist.atOffset(5)).to.be(entries[2]);
expect(() => skiplist.atOffset(6)).to.throwError();
skiplist.push(makeEntry(3));
expect(skiplist.atOffset(4)).to.be(entries[0]);
expect(skiplist.atOffset(5)).to.be(entries[4]);
expect(skiplist.atOffset(6)).to.be(entries[4]);
});
});