Skip to content
Snippets Groups Projects
index.spec.js 19.9 KiB
Newer Older
import chai from 'chai'
import getSensiplanStatus from '../../lib/sympto'
import { AssertionError } from 'assert'
Julia Friesel's avatar
Julia Friesel committed
import {
  cycleWithoutFhm,
  longAndComplicatedCycle,
  cycleWithTempAndNoMucusShift,
  cycleWithFhm,
  cycleWithoutAnyShifts,
  fiveDayCycle,
  cycleWithMucusOnFirstDay,
  mucusPeakAndFhmOnSameDay,
  fhmTwoDaysBeforeMucusPeak,
  fhm5DaysAfterMucusPeak,
  mucusPeak5DaysAfterFhm,
  mucusPeakTwoDaysBeforeFhm,
  fhmOnDay12,
Julia Friesel's avatar
Julia Friesel committed
  fhmOnDay15,
  mucusPeakSlightlyBeforeTempShift
Julia Friesel's avatar
Julia Friesel committed
} from './fixtures'

const expect = chai.expect

describe('sympto', () => {
Julia Friesel's avatar
Julia Friesel committed
  describe('with no previous higher measurement', () => {
    it('with no shifts detects only peri-ovulatory', function () {
      const status = getSensiplanStatus({
        cycle: cycleWithoutAnyShifts,
        previousCycle: cycleWithoutFhm
Julia Friesel's avatar
Julia Friesel committed
      })
Julia Friesel's avatar
Julia Friesel committed
      expect(status).to.eql({
Julia Friesel's avatar
Julia Friesel committed

Julia Friesel's avatar
Julia Friesel committed
        phases: {
          periOvulatory: {
            start: { date: '2018-06-01' },
            cycleDays: cycleWithoutAnyShifts
          }
        },
Julia Friesel's avatar
Julia Friesel committed
    })
    it('with shifts detects only peri-ovulatory and post-ovulatory', () => {
Julia Friesel's avatar
Julia Friesel committed
      const status = getSensiplanStatus({
        cycle: longAndComplicatedCycle,
        previousCycle: cycleWithoutFhm
Julia Friesel's avatar
Julia Friesel committed
      })
Julia Friesel's avatar
Julia Friesel committed
      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
Julia Friesel's avatar
Julia Friesel committed
      expect(Object.keys(status.phases).length).to.eql(2)
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date <= '2018-06-21')
Julia Friesel's avatar
Julia Friesel committed
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date >= '2018-06-21')
Julia Friesel's avatar
Julia Friesel committed
  describe('with previous higher measurement', () => {
    describe('with no shifts detects pre-ovulatory phase', function () {
      it('according to 5-day-rule', function () {
        const status = getSensiplanStatus({
          cycle: fiveDayCycle,
          previousCycle: cycleWithFhm
        })

        expect(Object.keys(status.phases).length).to.eql(1)
Julia Friesel's avatar
Julia Friesel committed

        expect(status.phases.preOvulatory).to.eql({
          cycleDays: fiveDayCycle,
          start: { date: '2018-06-01' },
          end: { date: '2018-06-05' }
        })
      })

    })
    describe('with no shifts detects pre- and peri-ovulatory phase', () => {
Julia Friesel's avatar
Julia Friesel committed
      it('according to 5-day-rule', function () {
        const status = getSensiplanStatus({
          cycle: cycleWithTempAndNoMucusShift,
          previousCycle: cycleWithFhm
Julia Friesel's avatar
Julia Friesel committed
        expect(Object.keys(status.phases).length).to.eql(2)
Julia Friesel's avatar
Julia Friesel committed

Julia Friesel's avatar
Julia Friesel committed
        expect(status.phases.preOvulatory).to.eql({
          cycleDays: cycleWithTempAndNoMucusShift
            .filter(({date}) => date <= '2018-06-05'),
Julia Friesel's avatar
Julia Friesel committed
          start: { date: '2018-06-01' },
          end: { date: '2018-06-05' }
        })
        expect(status.phases.periOvulatory).to.eql({
          cycleDays: cycleWithTempAndNoMucusShift
            .filter(({date}) => date > '2018-06-05'),
Julia Friesel's avatar
Julia Friesel committed
          start: { date: '2018-06-06' }
        })
      it('according to 5-day-rule with shortened pre-phase', function () {
        const status = getSensiplanStatus({
          cycle: cycleWithEarlyMucus,
          previousCycle: cycleWithFhm
        expect(Object.keys(status.phases).length).to.eql(2)
Julia Friesel's avatar
Julia Friesel committed

        expect(status.phases.preOvulatory).to.eql({
          cycleDays: [cycleWithEarlyMucus[0]],
          start: { date: '2018-06-01' },
          end: { date: '2018-06-01' }
        })
        expect(status.phases.periOvulatory).to.eql({
          cycleDays: cycleWithEarlyMucus.slice(1),
          start: { date: '2018-06-02' }
        })
      })
Julia Friesel's avatar
Julia Friesel committed
    })
Julia Friesel's avatar
Julia Friesel committed
    describe('with shifts detects pre- and peri-ovulatory phase', function () {
      it('according to 5-day-rule', function () {
        const status = getSensiplanStatus({
          cycle: longAndComplicatedCycle,
          previousCycle: cycleWithFhm
Julia Friesel's avatar
Julia Friesel committed
        })

        expect(Object.keys(status.phases).length).to.eql(3)
Julia Friesel's avatar
Julia Friesel committed

Julia Friesel's avatar
Julia Friesel committed
        expect(status.phases.preOvulatory).to.eql({
          cycleDays: longAndComplicatedCycle
            .filter(({date}) => date <= '2018-06-05'),
Julia Friesel's avatar
Julia Friesel committed
          start: { date: '2018-06-01' },
          end: { date: '2018-06-05' }
        })
        expect(status.phases.periOvulatory).to.eql({
          cycleDays: longAndComplicatedCycle
            .filter(({date}) => date > '2018-06-05' && date <= '2018-06-21'),
Julia Friesel's avatar
Julia Friesel committed
          start: { date: '2018-06-06' },
          end: { date: '2018-06-21', time: '18:00'}
        })
        expect(status.phases.postOvulatory).to.eql({
          cycleDays: longAndComplicatedCycle
            .filter(({date}) => date >= '2018-06-21'),
Julia Friesel's avatar
Julia Friesel committed
          start: { date: '2018-06-21', time: '18:00'}
        })
      })

    })

  describe('combining first higher measurment and mucus peak', () => {
    it('with fhM + mucus peak on same day finds start of postovu phase', () => {
      const status = getSensiplanStatus({
        cycle: mucusPeakAndFhmOnSameDay,
        previousCycle: cycleWithFhm
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-05' },
        cycleDays: mucusPeakAndFhmOnSameDay
          .filter(({date}) => date <= '2018-06-05')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-06' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: mucusPeakAndFhmOnSameDay
          .filter(({date}) => {
            return date > '2018-06-05' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: mucusPeakAndFhmOnSameDay
          .filter(({date}) => date >= '2018-06-21')
      })
    })

    it('with fhM 2 days before mucus peak waits for end of mucus eval', () => {
      const status = getSensiplanStatus({
        cycle: fhmTwoDaysBeforeMucusPeak,
        previousCycle: cycleWithFhm
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-05' },
        cycleDays: fhmTwoDaysBeforeMucusPeak
          .filter(({date}) => date <= '2018-06-05')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-06' },
        end: { date: '2018-06-26', time: '18:00' },
        cycleDays: fhmTwoDaysBeforeMucusPeak
          .filter(({date}) => {
            return date > '2018-06-05' && date <= '2018-06-26'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-26',
          time: '18:00'
        },
        cycleDays: fhmTwoDaysBeforeMucusPeak
          .filter(({date}) => date >= '2018-06-26')
Julia Friesel's avatar
Julia Friesel committed
    it('another example for mucus peak before temp shift', () => {
      const status = getSensiplanStatus({
        cycle: mucusPeakSlightlyBeforeTempShift,
        previousCycle: cycleWithFhm
Julia Friesel's avatar
Julia Friesel committed
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
Julia Friesel's avatar
Julia Friesel committed
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-05' },
        cycleDays: mucusPeakSlightlyBeforeTempShift
          .filter(({date}) => date <= '2018-06-05')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-06' },
        end: { date: '2018-06-17', time: '18:00' },
        cycleDays: mucusPeakSlightlyBeforeTempShift
          .filter(({date}) => {
            return date > '2018-06-05' && date <= '2018-06-17'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-17',
          time: '18:00'
        },
        cycleDays: mucusPeakSlightlyBeforeTempShift
          .filter(({date}) => date >= '2018-06-17')
      })
    })

    it('with another mucus peak 5 days after fHM ignores it', () => {
      const status = getSensiplanStatus({
        cycle: mucusPeak5DaysAfterFhm,
        previousCycle: cycleWithFhm
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-01' },
        cycleDays: mucusPeak5DaysAfterFhm
          .filter(({date}) => date <= '2018-06-01')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-02' },
        end: { date: '2018-06-22', time: '18:00' },
        cycleDays: mucusPeak5DaysAfterFhm
          .filter(({date}) => {
            return date > '2018-06-01' && date <= '2018-06-22'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-22',
          time: '18:00'
        },
        cycleDays: mucusPeak5DaysAfterFhm
          .filter(({date}) => date >= '2018-06-22')
      })
    })

    it('with mucus peak 2 days before fhM waits for end of temp eval', () => {
      const status = getSensiplanStatus({
        cycle:  mucusPeakTwoDaysBeforeFhm,
        previousCycle: cycleWithFhm
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-04' },
        cycleDays: mucusPeakTwoDaysBeforeFhm
          .filter(({date}) => date <= '2018-06-04')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-05' },
        end: { date: '2018-07-03', time: '18:00' },
        cycleDays: mucusPeakTwoDaysBeforeFhm
          .filter(({date}) => {
            return date > '2018-06-04' && date <= '2018-07-03'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-07-03',
          time: '18:00'
        },
        cycleDays: mucusPeakTwoDaysBeforeFhm
          .filter(({date}) => date >= '2018-07-03')
      })
    })

    it('with mucus peak 5 days before fhM waits for end of temp eval', () => {
      const status = getSensiplanStatus({
        cycle:  fhm5DaysAfterMucusPeak,
        previousCycle: cycleWithFhm
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-05' },
        cycleDays: fhm5DaysAfterMucusPeak
          .filter(({date}) => date <= '2018-06-05')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-06' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: fhm5DaysAfterMucusPeak
          .filter(({date}) => {
            return date > '2018-06-05' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: fhm5DaysAfterMucusPeak
          .filter(({date}) => date >= '2018-06-21')
  describe('applying the minus-8 rule', () => {
    it('shortens the pre-ovu phase if there is a previous <13 fhm', () => {
      const status = getSensiplanStatus({
        cycle:  longAndComplicatedCycle,
        previousCycle: fhmOnDay15,
        earlierCycles: [fhmOnDay12, ...Array(10).fill(fhmOnDay15)]
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-04' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date <= '2018-06-04')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-05' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => {
            return date > '2018-06-04' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date >= '2018-06-21')
      })
    })
    it('shortens pre-ovu phase with prev <13 fhm even with <12 cycles', () => {
      const status = getSensiplanStatus({
        cycle:  longAndComplicatedCycle,
        previousCycle: fhmOnDay12,
        earlierCycles: Array(10).fill(fhmOnDay12)
      })

      expect(status.temperatureShift).to.be.an('object')
      expect(status.mucusShift).to.be.an('object')
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-04' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date <= '2018-06-04')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-05' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => {
            return date > '2018-06-04' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date >= '2018-06-21')
      })
    })
    it('shortens the pre-ovu phase if mucus occurs', () => {
      const status = getSensiplanStatus({
        cycle: cycleWithEarlyMucus,
        previousCycle: fhmOnDay12,
        earlierCycles: Array(10).fill(fhmOnDay12)
      expect(Object.keys(status.phases).length).to.eql(2)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-01' },
        cycleDays: cycleWithEarlyMucus
          .filter(({date}) => date <= '2018-06-01')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-02' },
        cycleDays: cycleWithEarlyMucus
          .filter(({date}) => {
            return date > '2018-06-01'
          })
      })
    })

    it('shortens the pre-ovu phase if mucus occurs even on the first day', () => {
      const status = getSensiplanStatus({
        cycle: cycleWithMucusOnFirstDay,
        previousCycle: fhmOnDay12,
        earlierCycles: Array(10).fill(fhmOnDay12)
      })


      expect(Object.keys(status.phases).length).to.eql(1)

      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-01' },
        cycleDays: cycleWithMucusOnFirstDay
      })
    })

    it('lengthens the pre-ovu phase if >= 12 cycles with fhm > 13', () => {
      const status = getSensiplanStatus({
        cycle: longAndComplicatedCycle,
        previousCycle: fhmOnDay15,
        earlierCycles: Array(11).fill(fhmOnDay15)
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-07' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date <= '2018-06-07')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-08' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => {
            return date > '2018-06-07' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date >= '2018-06-21')
      })
    })

    it('does not lengthen the pre-ovu phase if < 12 cycles', () => {
      const status = getSensiplanStatus({
        cycle: longAndComplicatedCycle,
        previousCycle: fhmOnDay15,
        earlierCycles: Array(10).fill(fhmOnDay15)
      expect(Object.keys(status.phases).length).to.eql(3)
      expect(status.phases.preOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-05' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date <= '2018-06-05')
      })
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-06' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => {
            return date > '2018-06-05' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date >= '2018-06-21')
      })
    })

    it('does not detect any pre-ovu phase if prev cycle had no fhm', () => {
      const status = getSensiplanStatus({
        cycle: longAndComplicatedCycle,
        previousCycle: cycleWithoutFhm,
        earlierCycles: [...Array(12).fill(fhmOnDay15)]
      expect(Object.keys(status.phases).length).to.eql(2)
      expect(status.phases.periOvulatory).to.eql({
        start: { date: '2018-06-01' },
        end: { date: '2018-06-21', time: '18:00' },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => {
            return date >= '2018-06-01' && date <= '2018-06-21'
          })
      })
      expect(status.phases.postOvulatory).to.eql({
        start: {
          date: '2018-06-21',
          time: '18:00'
        },
        cycleDays: longAndComplicatedCycle
          .filter(({date}) => date >= '2018-06-21')
Julia Friesel's avatar
Julia Friesel committed
  describe('when args are wrong', () => {
    it('throws when arg object is not in right format', () => {
      const wrongObject = { hello: 'world' }
      expect(() => getSensiplanStatus(wrongObject)).to.throw(AssertionError)
Julia Friesel's avatar
Julia Friesel committed
    })
    it('throws if cycle array is empty', () => {
      expect(() => getSensiplanStatus({cycle: []})).to.throw(AssertionError)
Julia Friesel's avatar
Julia Friesel committed
    })
    it('throws if cycle days are not in right format', () => {
      expect(() => getSensiplanStatus({
        cycle: [{
          hello: 'world',
          bleeding: { value: 0 }
        }],
        earlierCycles: [[{
Julia Friesel's avatar
Julia Friesel committed
          date: '1992-09-09',
          bleeding: { value: 0 }
      })).to.throw(AssertionError)
Julia Friesel's avatar
Julia Friesel committed
      expect(() => getSensiplanStatus({
        cycle: [{
          date: '2018-04-13',
          temperature: {value: '35'},
          bleeding: { value: 0 }
        }],
        earlierCycles: [[{
Julia Friesel's avatar
Julia Friesel committed
          date: '1992-09-09',
          bleeding: { value: 0 }
      })).to.throw(AssertionError)
Julia Friesel's avatar
Julia Friesel committed
      expect(() => getSensiplanStatus({
        cycle: [{
          date: '09-14-2017',
          bleeding: { value: 0 }
        }],
        earlierCycles: [[{
Julia Friesel's avatar
Julia Friesel committed
          date: '1992-09-09',
          bleeding: { value: 0 }
      })).to.throw(AssertionError)
Julia Friesel's avatar
Julia Friesel committed
    })
    it('throws if first cycle day does not have bleeding value', () => {
      expect(() => getSensiplanStatus({
        cycle: [{
          date: '2017-01-01',
          bleeding: {
            value: 'medium'
          }
        }],
        earlierCycles: [[
Julia Friesel's avatar
Julia Friesel committed
          {
            date: '2017-09-23',
          }
      })).to.throw(AssertionError)