Files
2026-06-24 09:48:54 +02:00

140 lines
4.0 KiB
JavaScript

var _ = require('lodash');
var fuzzer = require('ot-fuzzer');
var richType = require('../lib/type');
var Delta = richType.Delta;
var FORMATS = {
color: ['red', 'orange', 'yellow', 'green', 'blue', 'purple', null],
font: ['serif', 'sans-serif', 'monospace', null],
bold: [true, null],
italic: [true, null]
};
function generateRandomFormat (includeNull) {
var format = {};
for (var key in FORMATS) {
if (fuzzer.randomReal() < 0.5) {
var value = FORMATS[key][fuzzer.randomInt(FORMATS[key].length)];
if (value || includeNull) {
format[key] = value;
}
}
}
return Object.keys(format).length > 0 ? format : undefined;
};
function generateRandomOp (snapshot) {
snapshot = _.cloneDeep(snapshot);
var length = snapshot.ops.reduce(function(length, op) {
if (!op.insert) {
console.error(snapshot);
throw new Error('Snapshot should only have inserts');
}
// Snapshot should only have inserts
return length + (_.isString(op.insert) ? op.insert.length : 1);
}, 0);
var base = length > 100 ? 10 : 7; // Favor deleting on long documents
var delta = new Delta();
var result = new Delta();
do {
// Allows insert/delete to occur at the end (deletes will be noop)
var modIndex = fuzzer.randomInt(Math.min(length, 5) + 1);
length -= modIndex;
var modLength = Math.min(length, fuzzer.randomInt(4) + 1);
delta.retain(modIndex);
var ops = next(snapshot, modIndex);
for (var i in ops) {
result.push(ops[i]);
}
switch (fuzzer.randomInt(base)) {
case 0:
// Insert plain text
var word = fuzzer.randomWord();
delta.insert(word);
result.insert(word);
break;
case 1:
// Insert formatted text
var word = fuzzer.randomWord();
var formats = generateRandomFormat(false);
delta.insert(word, formats);
result.insert(word, formats);
break;
case 2:
// Insert embed
var type = fuzzer.randomInt(2) + 1;
var formats = generateRandomFormat(false);
delta.insert(type, formats);
result.insert(type, formats);
break;
case 3: case 4:
var attributes = generateRandomFormat(true);
delta.retain(modLength, attributes);
ops = next(snapshot, modLength);
for (var i in ops) {
ops[i].attributes = ops[i].attributes || {};
for (var key in attributes) {
ops[i].attributes[key] = (attributes[key] === null) ? undefined : attributes[key];
}
ops[i].attributes = _.reduce(ops[i].attributes, function (memo, value, key) {
if (value !== null && value !== undefined) {
memo[key] = value;
}
return memo;
}, {});
var newOp = { insert: ops[i].insert };
if (_.keys(ops[i].attributes).length > 0) newOp.attributes = ops[i].attributes;
result.push(newOp);
}
length -= modLength;
break;
default:
next(snapshot, modLength);
delta.delete(modLength);
length -= modLength;
break;
}
} while (length > 0 && fuzzer.randomInt(2) > 0);
for (var i in snapshot.ops) {
result.push(snapshot.ops[i]);
}
return [delta, result];
};
function next (snapshot, length) {
var ops = [];
while (length > 0) {
var opLength;
if (_.isString(snapshot.ops[0].insert)) {
if (length >= snapshot.ops[0].insert.length) {
opLength = snapshot.ops[0].insert.length;
ops.push(snapshot.ops.shift());
} else {
var insert = snapshot.ops[0].insert.substr(0, length);
snapshot.ops[0].insert = snapshot.ops[0].insert.substr(insert.length);
opLength = insert.length;
var op = { insert: insert };
if (snapshot.ops[0].attributes) {
op.attributes = _.clone(snapshot.ops[0].attributes);
}
ops.push(op);
}
} else {
ops.push(snapshot.ops.shift());
opLength = 1;
}
length -= opLength;
}
return ops;
};
fuzzer(richType, generateRandomOp, 100);