Home Reference Source Test

specs/lens.spec.js

import { assert } from 'chai';

import { prop, lensIndex, lensProp, assoc, assocPath,
  path, pathEq, pathOr, pathSatisfies } from '../src/Lens';
import { IncorrectArgsTypeError } from '../src/Error/incorrect-args-type-error';


/** @test {Lens} */
describe('Lens Library Tests', () => {

  /** @test {Lens#prop} */
  it('test: prop(propName, source)', () => {
    let user = { name: 'Oleh' };
    assert.equal('Oleh', prop('name', user));
  });

  /** @test {Lens#prop} */
  it('test: prop(propName, source) with inner object', () => {
    let user = { name: { first: 'Oleh' } };
    assert.notEqual({ first: 'Oleh' }, prop('name', user));
    assert.equal('Oleh', prop('name', user).first);
  });

  /** @test {Lens#prop} */
  it('test: prop(propName, source) with wrong first argument', () => {
    assert.throw(() => prop(1, {}), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#prop} */
  it('test: prop(propName, source) with wrong second argument', () => {
    assert.throw(() => prop('some', []), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#prop} */
  it('test: prop(propName, source) with undefined first argument', () => {
    assert.throw(() => prop(undefined, 2), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#prop} */
  it('test: prop(propName, source) with undefined second argument', () => {
    assert.throw(() => prop('hello', undefined), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensIndex} */
  it('test: lensIndex(index, source)', () => {
    let names = ['Oleh', 'John'];
    assert.equal('Oleh', lensIndex(0, names));
  });

  /** @test {Lens#lensIndex} */
  it('test: lensIndex(index, source) with inner object', () => {
    let expected = { name: { first: 'Oleh' } };
    let names = [expected];
    assert.notEqual(expected, lensIndex(0, names));
    assert.equal(expected.name.first, lensIndex(0, names).name.first);
  });

  /** @test {Lens#lensIndex} */
  it('test: lensIndex(index, source) with wrong first argument', () => {
    assert.throw(() => lensIndex('propName', []), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensIndex} */
  it('test: lensIndex(index, source) with wrong second argument', () => {
    assert.throw(() => prop(1, {}), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensIndex} */
  it('test: lensIndex(index, source) with undefined first argument', () => {
    assert.throw(() => lensIndex(undefined, []), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensIndex} */
  it('test: lensIndex(index, source) with undefined second argument', () => {
    assert.throw(() => lensIndex(1, undefined), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensProp} */
  it('test: lensProp(propName)', () => {
    let user1 = { name: 'John' }
      , user2 = { name: 'Eric' }
      , nameLens = lensProp('name');
    assert.equal('John', nameLens(user1));
    assert.equal('Eric', nameLens(user2));
  });

  /** @test {Lens#lensProp} */
  it('test: lensProp(propName) with wrong first argument', () => {
    assert.throw(() => lensProp(1), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensProp} */
  it('test: lensProp(propName) with wrong second argument', () => {
    assert.throw(() => lensProp('some')(2), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensProp} */
  it('test: lensProp(propName) with undefined first argument', () => {
    assert.throw(() => lensProp(undefined), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#lensProp} */
  it('test: lensProp(propName) with undefined first argument', () => {
    assert.throw(() => lensProp('hello')(undefined), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#assoc} */
  it('test: assoc(propName, valueToBeSet, source)', () => {
    let user = { name: 'John' }
      , updatedUser = assoc('name', 'Eric', user);
    assert.equal('Eric', updatedUser.name);
    assert.equal('John', user.name);
    assert.notEqual(user, updatedUser);
  });

  /** @test {Lens#assoc} */
  it('test: assoc(propName, valueToBeSet, source) with inner object', () => {
    let user = { name: { first: 'John' } }
      , updatedUser =  assoc('name', { first: 'Eric' }, user)

    assert.notEqual(user.name, updatedUser.name);
    assert.equal('John', user.name.first);
    assert.equal('Eric', updatedUser.name.first);
  });

  /** @test {Lens#assoc} */
  it('test: assoc(propName, valueToBeSet, source) with wrong first argument', () => {
    assert.throw(() => assoc(1, {}, {}), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#assoc} */
  it('test: assoc(propName, valueToBeSet, source) with wrong third argument', () => {
    assert.throw(() => assoc('some', {}, 'not object'), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#assoc} */
  it('test: assoc(propName, valueToBeSet, source) with undefined first argument', () => {
    assert.throw(() => assoc(undefined, 2, {}), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#assoc} */
  it('test: assoc(propName, valueToBeSet, source) with undefined third argument', () => {
    assert.throw(() => prop('hello', 3, undefined), IncorrectArgsTypeError, 'Incorrect arguments were passed');
  });

  /** @test {Lens#assocPath} */
  it('test: assocPath(path, valueToBeSet, source)', () => {
    let user = {name: 'Oleh'}
      , result1 = assocPath(['a', 1, 'b', 'c'], 'hello', user)
      , result2 = assocPath(['a', 1, 'b', 'c', 0], 'world', user)
      , result3 = assocPath(['a', 1, 3, 'b', 'c', 0, 'r'], 'yeah', user);

    assert.equal(result1.a[1].b.c, 'hello');
    assert.equal(result2.a[1].b.c[0], 'world');
    assert.equal(result3.a[1][3].b.c[0].r, 'yeah');
    assert.notEqual(result1, user);
    assert.notEqual(result2, user);
    assert.notEqual(result3, user);
  });

  /** @test {Lens#assocPath} */
  it('test: assocPath(path, valueToBeSet, source) with array', () => {
    let user = [{name: 'John'}, {name: 'Jimi'}]
      , result = assocPath([1, 1, 'b', 'd', 1, 'c'], 'hello', user);

    assert.equal(result[1][1].b.d[1].c, 'hello');

    // should not override old values
    assert.equal(result[0].name, 'John');

  });

  /** @test {Lens#assocPath} */
  it('test: assocPath(path, valueToBeSet, source) should not override old props', () => {
    let user = {
      d: { r: [ 1, { b:2 } ] },
      a: [
        [ { b:2 } ],
        [ { b: { c: [{ r: 'ddd' } ] } }, { b: { c: [ { r: 'bbb' } ] } }, { b: { c: [ { r: 'aaa' } ] } } ]
      ]
    }
      , result = assocPath(['a', 1, 2, 'b', 'c', 0, 'r'], 'yeah', user);

    // should not override old object
    assert.equal(user.a[1][2].b.c[0].r, 'aaa');
    assert.equal(user.a[1][1].b.c[0].r, 'bbb');
    assert.equal(user.a[1][0].b.c[0].r, 'ddd');
    assert.equal(user.d.r[1].b, 2);

    // should override only one propperty
    assert.equal(result.a[1][2].b.c[0].r, 'yeah');

    // rest should stay the same as in source
    assert.equal(result.a[1][1].b.c[0].r, 'bbb');
    assert.equal(result.a[1][0].b.c[0].r, 'ddd');
    assert.equal(result.d.r[1].b, 2);

    // full identity
    assert.notEqual(JSON.stringify(user), JSON.stringify(result));

    result = assocPath(['a', 1, 2, 'b', 'c', 0, 'r'], 'aaa', user);

    assert.equal(JSON.stringify(user), JSON.stringify(result));
    assert.deepEqual(user, result);
  });

  /** @test {Lens#path} */
  it('test: path(p, source)', () => {
    let user = { name: 'John', address: { email: 'some@email.com' }}
      , result = path(['address', 'email'], user);
    assert.equal(result, 'some@email.com');
    assert.isUndefined(path(['address', 'email', 'notExisting'], user));
  });

  /** @test {Lens#pathEq} */
  it('test: pathEq(path, source, value)', () => {
    let user = { name: 'John', address: { email: 'some@email.com' }}
      , result1 = pathEq(['address', 'email'], user, 'not email')
      , result2 = pathEq(['address', 'email'], user, 'some@email.com');
    assert.isFalse(result1);
    assert.isTrue(result2, 'some@email.com');
  });

  /** @test {Lens#pathOr} */
  it('test: pathOr(path, source, orValue)', () => {
    let user = { name: 'John', address: { email: 'some@email.com' }};
    assert.equal(pathOr(['address', 'email'], user, 'N/A'), 'some@email.com');
    assert.equal(pathOr(['address', 'email', 'notExisting'], user, 'N/A'), 'N/A');
  });

  /** @test {Lens#pathSatisfies} */
  it('test: pathSatisfies(path, source, satisfyFn)', () => {
    let user = { name: 'John', address: { email: 'some@email.com' }};
    assert.equal(pathSatisfies(['address', 'email'], user, p => p.toUpperCase()), 'SOME@EMAIL.COM');
  });
});