| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- // @ts-check
- const { expect } = require('chai')
- const {
- RetainOp,
- ScanOp,
- InsertOp,
- RemoveOp,
- } = require('../lib/operation/scan_op')
- const { UnprocessableError, ApplyError } = require('../lib/errors')
- const TrackingProps = require('../lib/file_data/tracking_props')
- describe('ScanOp', function () {
- describe('fromJSON', function () {
- it('constructs a RetainOp from object', function () {
- const op = ScanOp.fromJSON({ r: 1 })
- expect(op).to.be.instanceOf(RetainOp)
- expect(/** @type {RetainOp} */ (op).length).to.equal(1)
- })
- it('constructs a RetainOp from number', function () {
- const op = ScanOp.fromJSON(2)
- expect(op).to.be.instanceOf(RetainOp)
- expect(/** @type {RetainOp} */ (op).length).to.equal(2)
- })
- it('constructs an InsertOp from string', function () {
- const op = ScanOp.fromJSON('abc')
- expect(op).to.be.instanceOf(InsertOp)
- expect(/** @type {InsertOp} */ (op).insertion).to.equal('abc')
- })
- it('constructs an InsertOp from object', function () {
- const op = ScanOp.fromJSON({ i: 'abc' })
- expect(op).to.be.instanceOf(InsertOp)
- expect(/** @type {InsertOp} */ (op).insertion).to.equal('abc')
- })
- it('constructs a RemoveOp from number', function () {
- const op = ScanOp.fromJSON(-2)
- expect(op).to.be.instanceOf(RemoveOp)
- expect(/** @type {RemoveOp} */ (op).length).to.equal(2)
- })
- it('throws an error for invalid input', function () {
- expect(() => ScanOp.fromJSON(/** @type {any} */ ({}))).to.throw(
- UnprocessableError
- )
- })
- it('throws an error for zero', function () {
- expect(() => ScanOp.fromJSON(0)).to.throw(UnprocessableError)
- })
- })
- })
- describe('RetainOp', function () {
- it('is equal to another RetainOp with the same length', function () {
- const op1 = new RetainOp(1)
- const op2 = new RetainOp(1)
- expect(op1.equals(op2)).to.be.true
- })
- it('is not equal to another RetainOp with a different length', function () {
- const op1 = new RetainOp(1)
- const op2 = new RetainOp(2)
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another RetainOp with no tracking info', function () {
- const op1 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new RetainOp(4)
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another RetainOp with different tracking info', function () {
- const op1 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user2', new Date('2024-01-01T00:00:00.000Z'))
- )
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to an InsertOp', function () {
- const op1 = new RetainOp(1)
- const op2 = new InsertOp('a')
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to a RemoveOp', function () {
- const op1 = new RetainOp(1)
- const op2 = new RemoveOp(1)
- expect(op1.equals(op2)).to.be.false
- })
- it('can merge with another RetainOp', function () {
- const op1 = new RetainOp(1)
- const op2 = new RetainOp(2)
- expect(op1.canMergeWith(op2)).to.be.true
- op1.mergeWith(op2)
- expect(op1.equals(new RetainOp(3))).to.be.true
- })
- it('cannot merge with another RetainOp if the tracking user is different', function () {
- const op1 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user2', new Date('2024-01-01T00:00:00.000Z'))
- )
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('can merge with another RetainOp if the tracking user is the same', function () {
- const op1 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new RetainOp(
- 4,
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:01.000Z'))
- )
- op1.mergeWith(op2)
- expect(
- op1.equals(
- new RetainOp(
- 8,
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:00.000Z')
- )
- )
- )
- ).to.be.true
- })
- it('cannot merge with an InsertOp', function () {
- const op1 = new RetainOp(1)
- const op2 = new InsertOp('a')
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('cannot merge with a RemoveOp', function () {
- const op1 = new RetainOp(1)
- const op2 = new RemoveOp(1)
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('can be converted to JSON', function () {
- const op = new RetainOp(3)
- expect(op.toJSON()).to.equal(3)
- })
- it('adds to the length and cursor when applied to length', function () {
- const op = new RetainOp(3)
- const { length, inputCursor } = op.applyToLength({
- length: 10,
- inputCursor: 10,
- inputLength: 30,
- })
- expect(length).to.equal(13)
- expect(inputCursor).to.equal(13)
- })
- })
- describe('InsertOp', function () {
- it('is equal to another InsertOp with the same insertion', function () {
- const op1 = new InsertOp('a')
- const op2 = new InsertOp('a')
- expect(op1.equals(op2)).to.be.true
- })
- it('is not equal to another InsertOp with a different insertion', function () {
- const op1 = new InsertOp('a')
- const op2 = new InsertOp('b')
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with no tracking info', function () {
- const op1 = new InsertOp(
- 'a',
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new InsertOp('a')
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with different tracking info', function () {
- const op1 = new InsertOp(
- 'a',
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new InsertOp(
- 'a',
- new TrackingProps('insert', 'user2', new Date('2024-01-01T00:00:00.000Z'))
- )
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with no comment ids', function () {
- const op1 = new InsertOp('a', undefined, ['1'])
- const op2 = new InsertOp('a')
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with tracking info', function () {
- const op1 = new InsertOp('a', undefined)
- const op2 = new InsertOp(
- 'a',
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with comment ids', function () {
- const op1 = new InsertOp('a')
- const op2 = new InsertOp('a', undefined, ['1'])
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with different comment ids', function () {
- const op1 = new InsertOp('a', undefined, ['1'])
- const op2 = new InsertOp('a', undefined, ['2'])
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to another InsertOp with overlapping comment ids', function () {
- const op1 = new InsertOp('a', undefined, ['1'])
- const op2 = new InsertOp('a', undefined, ['2', '1'])
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to a RetainOp', function () {
- const op1 = new InsertOp('a')
- const op2 = new RetainOp(1)
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to a RemoveOp', function () {
- const op1 = new InsertOp('a')
- const op2 = new RemoveOp(1)
- expect(op1.equals(op2)).to.be.false
- })
- it('can merge with another InsertOp', function () {
- const op1 = new InsertOp('a')
- const op2 = new InsertOp('b')
- expect(op1.canMergeWith(op2)).to.be.true
- op1.mergeWith(op2)
- expect(op1.equals(new InsertOp('ab'))).to.be.true
- })
- it('cannot merge with another InsertOp if comment id info is different', function () {
- const op1 = new InsertOp('a', undefined, ['1'])
- const op2 = new InsertOp('b', undefined, ['1', '2'])
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('cannot merge with another InsertOp if comment id info is different while tracking info matches', function () {
- const op1 = new InsertOp(
- 'a',
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:00.000Z')
- ),
- ['1', '2']
- )
- const op2 = new InsertOp(
- 'b',
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:00.000Z')
- ),
- ['3']
- )
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('cannot merge with another InsertOp if comment id is present in other and tracking info matches', function () {
- const op1 = new InsertOp(
- 'a',
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new InsertOp(
- 'b',
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:00.000Z')
- ),
- ['1']
- )
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('cannot merge with another InsertOp if tracking user is different', function () {
- const op1 = new InsertOp(
- 'a',
- new TrackingProps('insert', 'user1', new Date('2024-01-01T00:00:00.000Z'))
- )
- const op2 = new InsertOp(
- 'b',
- new TrackingProps('insert', 'user2', new Date('2024-01-01T00:00:00.000Z'))
- )
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('can merge with another InsertOp if tracking user and comment info is the same', function () {
- const op1 = new InsertOp(
- 'a',
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:00.000Z')
- ),
- ['1', '2']
- )
- const op2 = new InsertOp(
- 'b',
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:01.000Z')
- ),
- ['1', '2']
- )
- expect(op1.canMergeWith(op2)).to.be.true
- op1.mergeWith(op2)
- expect(
- op1.equals(
- new InsertOp(
- 'ab',
- new TrackingProps(
- 'insert',
- 'user1',
- new Date('2024-01-01T00:00:00.000Z')
- ),
- ['1', '2']
- )
- )
- ).to.be.true
- })
- it('cannot merge with a RetainOp', function () {
- const op1 = new InsertOp('a')
- const op2 = new RetainOp(1)
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('cannot merge with a RemoveOp', function () {
- const op1 = new InsertOp('a')
- const op2 = new RemoveOp(1)
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('can be converted to JSON', function () {
- const op = new InsertOp('a')
- expect(op.toJSON()).to.equal('a')
- })
- it('adds to the length when applied to length', function () {
- const op = new InsertOp('abc')
- const { length, inputCursor } = op.applyToLength({
- length: 10,
- inputCursor: 20,
- inputLength: 40,
- })
- expect(length).to.equal(13)
- expect(inputCursor).to.equal(20)
- })
- it('can apply a retain of the rest of the input', function () {
- const op = new RetainOp(10)
- const { length, inputCursor } = op.applyToLength({
- length: 10,
- inputCursor: 5,
- inputLength: 15,
- })
- expect(length).to.equal(20)
- expect(inputCursor).to.equal(15)
- })
- it('cannot apply to length if the input cursor is at the end', function () {
- const op = new RetainOp(10)
- expect(() =>
- op.applyToLength({
- length: 10,
- inputCursor: 10,
- inputLength: 10,
- })
- ).to.throw(ApplyError)
- })
- })
- describe('RemoveOp', function () {
- it('is equal to another RemoveOp with the same length', function () {
- const op1 = new RemoveOp(1)
- const op2 = new RemoveOp(1)
- expect(op1.equals(op2)).to.be.true
- })
- it('is not equal to another RemoveOp with a different length', function () {
- const op1 = new RemoveOp(1)
- const op2 = new RemoveOp(2)
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to a RetainOp', function () {
- const op1 = new RemoveOp(1)
- const op2 = new RetainOp(1)
- expect(op1.equals(op2)).to.be.false
- })
- it('is not equal to an InsertOp', function () {
- const op1 = new RemoveOp(1)
- const op2 = new InsertOp('a')
- expect(op1.equals(op2)).to.be.false
- })
- it('can merge with another RemoveOp', function () {
- const op1 = new RemoveOp(1)
- const op2 = new RemoveOp(2)
- expect(op1.canMergeWith(op2)).to.be.true
- op1.mergeWith(op2)
- expect(op1.equals(new RemoveOp(3))).to.be.true
- })
- it('cannot merge with a RetainOp', function () {
- const op1 = new RemoveOp(1)
- const op2 = new RetainOp(1)
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('cannot merge with an InsertOp', function () {
- const op1 = new RemoveOp(1)
- const op2 = new InsertOp('a')
- expect(op1.canMergeWith(op2)).to.be.false
- expect(() => op1.mergeWith(op2)).to.throw(Error)
- })
- it('can be converted to JSON', function () {
- const op = new RemoveOp(3)
- expect(op.toJSON()).to.equal(-3)
- })
- it('adds to the input cursor when applied to length', function () {
- const op = new RemoveOp(3)
- const { length, inputCursor } = op.applyToLength({
- length: 10,
- inputCursor: 10,
- inputLength: 30,
- })
- expect(length).to.equal(10)
- expect(inputCursor).to.equal(13)
- })
- })
|