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     64020x 61008x       17449x 17449x 17449x 5821x   11628x         22878x 22878x 22878x 36196x 18747x 18747x 18747x   17449x     22878x       6072x 6072x 13731x 13731x   6072x       4656x 4656x 3365x 3365x 3365x   4656x       6817x 6817x   6817x 5429x       6914x 6914x           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));
  }
}