All files / collaborative-prosemirror/src/__tests__ fuzzer.ts

100% Statements 48/48
100% Branches 16/16
100% Functions 12/12
100% Lines 42/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97  3x 3x                               3x   3x 449x   449x   449x 449x     63542x 60632x       17346x 17346x 17346x 5746x   11600x         22876x 22876x 22876x 36133x 18787x 18787x 18787x   17346x     22876x       6015x 6015x 13666x 13666x   6015x       4482x 4482x 3246x 3246x 3246x   4482x       6864x 6864x   6864x 5530x       6802x 6802x           449x      
import type {Node} from 'prosemirror-model';
import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer';
import {
  doc,
  blockquote,
  ul,
  ol,
  li,
  p,
  pre,
  h1,
  h2,
  h3,
  em,
  strong,
  type MarkBuilder,
  a,
} from 'prosemirror-test-builder';
import {RandomJson} from '@jsonjoy.com/json-random';
 
export class NodeToViewRangeFuzzer {
  public static readonly doc = () => new NodeToViewRangeFuzzer().createDocumentNode();
 
  private fuzzer = new Fuzzer();
 
  private nodeCount = 0;
  private maxNodeCount = 100;
 
  doContinue(percent = 50): boolean {
    if (this.nodeCount >= this.maxNodeCount) return false;
    return this.fuzzer.randomInt(1, 100) <= percent;
  }
 
  createInlineNode(): ReturnType<MarkBuilder> {
    const builder = this.fuzzer.pick([em, strong, a]);
    this.nodeCount++;
    if (builder === a) {
      return a({href: RandomJson.genString(4)}, ...this.createInlineFragment());
    } else {
      return builder(...this.createInlineFragment());
    }
  }
 
  createInlineFragment(percent = 90): (ReturnType<MarkBuilder> | string)[] {
    const nodes: (ReturnType<MarkBuilder> | string)[] = [];
    const count = Fuzzer.randomInt(1, 5);
    for (let i = 0; i < count; i++) {
      if (!this.doContinue()) {
        this.nodeCount++;
        nodes.push(RandomJson.genString(5));
        break;
      } else {
        nodes.push(this.createInlineNode());
      }
    }
    return nodes;
  }
 
  createBlockFragment(percent = 60): Node[] {
    const nodes: Node[] = [];
    while (this.doContinue(percent)) {
      const node = this.fuzzer.pick([() => this.createLeafBlockNode(), () => this.createContainerBlockNode()]);
      nodes.push(node());
    }
    return nodes;
  }
 
  createListFragment(percent = 50): Node[] {
    const nodes: Node[] = [];
    while (this.doContinue(percent)) {
      percent = Math.max(0, percent - 10);
      this.nodeCount++;
      nodes.push(li(...this.createBlockFragment()));
    }
    return nodes;
  }
 
  createLeafBlockNode(): Node {
    const builder = this.fuzzer.pick([p, pre, h1, h2, h3]);
    this.nodeCount++;
    // code_block (pre) disallows marks — use plain text only.
    if (builder === pre) return pre(RandomJson.genString(8));
    return builder(...this.createInlineFragment());
  }
 
  createContainerBlockNode(): Node {
    const builder = this.fuzzer.pick([blockquote, ul, ol]);
    return builder === ul || builder === ol
      ? builder(...this.createListFragment())
      : builder(...this.createBlockFragment());
  }
 
  createDocumentNode(): Node {
    return doc(...this.createBlockFragment(100));
  }
}