Skip to content

Commit

Permalink
Splits the Base State scenarios into the current 4 known scenarios (#266
Browse files Browse the repository at this point in the history
)

- Splits the Base State scenarios into the current 4 known scenarios (more refinement TBD)
- Updates scenarios to be numbered
- Converts scenario type enum from an enum with string members to an enum with number members
- Adds a type alias for i18n translation strings so that we can use the type `I18nString` instead of just `string` for extra clarity
  • Loading branch information
rocketnova authored Jul 6, 2021
1 parent 2b3a5b8 commit 16c3ee3
Show file tree
Hide file tree
Showing 13 changed files with 290 additions and 142 deletions.
3 changes: 2 additions & 1 deletion components/TransLine.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Trans } from 'react-i18next'
import React from 'react'
import { I18nString } from '../types/common'

export interface TransLineProps {
i18nKey: string // Expects an i18n key
i18nKey: I18nString
links?: string[] | null
}

Expand Down
28 changes: 21 additions & 7 deletions public/locales/en/claim-status.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
{
"pending-determination": {
"description": "We need to confirm your eligibility for benefits."
"scenarios": {
"scenario1": {
"description": "We need to confirm your eligibility for benefits."
},
"scenario7": {
"description": "There are no pending issues at this time."
},
"scenario8": {
"description": "Your claim is active and there are no pending issues at this time."
},
"scenario9": {
"description": "Your payments are pending further review.",
"next-step": "Check your UI Online inbox for the latest updates."
},
"scenario10": {
"description": "[this copy may or may not be unique] Your payments are pending further review."
}
},
"base-pending": {
"description": "Your payments are pending further review."
},
"base-no-pending": {
"description": "There are no pending issues at this time."
"conditional-next-steps": {
"certify-no-pending": "<0>Keep certifying</0> as long as you need benefits.",
"certify-pending": "<0>Keep certifying</0> as long as you need benefits, so your payments are not delayed further.",
"contact-info": "Make sure we have your current phone number and email address."
}
}
26 changes: 19 additions & 7 deletions public/locales/es/claim-status.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
{
"pending-determination": {
"description": "Necesitamos confirmar su elegibilidad para los beneficios."
"scenarios": {
"scenario1": {
"description": "Necesitamos confirmar su elegibilidad para los beneficios."
},
"scenario7": {
"description": "No hay asuntos pendientes en este momento."
},
"scenario8": {
"description": "Su reclamo está activo y no hay problemas pendientes en este momento."
},
"scenario9": {
"description": "Sus pagos están pendientes de revisión adicional."
},
"scenario10": {
"description": "Sus pagos están pendientes de revisión adicional."
}
},
"base-pending": {
"description": "Sus pagos están pendientes de revisión adicional."
},
"base-no-pending": {
"description": "No hay asuntos pendientes en este momento."
"conditional-next-steps": {
"certify-no-pending": "<0>Siga certificando</0> mientras necesite beneficios.",
"certify-pending": "<0>Siga certificando</0> mientras necesite beneficios, para que sus pagos no se retrasen más."
}
}
14 changes: 7 additions & 7 deletions stories/Page.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Story, Meta } from '@storybook/react'
import { withNextRouter } from 'storybook-addon-next-router'

import Home, { HomeProps } from '../pages/index'
import getScenarioContent, { ScenarioType, ScenarioTypeKey } from '../utils/getScenarioContent'
import getScenarioContent, { ScenarioType, ScenarioTypeNames } from '../utils/getScenarioContent'
import apiGatewayStub from '../utils/apiGatewayStub'
import { getNumericEnumKeys } from '../utils/numericEnum'

// See https://storybook.js.org/docs/riot/essentials/controls#dealing-with-complex-values
export default {
Expand All @@ -12,24 +13,23 @@ export default {
decorators: [withNextRouter],
argTypes: {
scenario: {
options: Object.keys(ScenarioType),
mapping: Object.keys(ScenarioType), // return the key instead of the value
defaultValue: 'BaseNoPending',
options: getNumericEnumKeys(ScenarioType),
defaultValue: 0,
control: {
type: 'select',
labels: ScenarioType,
labels: ScenarioTypeNames,
},
},
},
} as Meta

// Extend HomeProps to add a complex story value
interface StoryHomeProps extends HomeProps {
scenario: ScenarioTypeKey
scenario: number
}

const Template: Story<StoryHomeProps> = ({ ...args }) => {
args.scenarioContent = getScenarioContent(apiGatewayStub(ScenarioType[args.scenario]))
args.scenarioContent = getScenarioContent(apiGatewayStub(args.scenario))
return <Home {...args} />
}

Expand Down
2 changes: 1 addition & 1 deletion tests/pages/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jest.mock('next/router', () => ({
let scenarioContent: ScenarioContent

beforeAll(() => {
scenarioContent = getScenarioContent(apiGatewayStub(ScenarioType.PendingDetermination))
scenarioContent = getScenarioContent(apiGatewayStub(ScenarioType.Scenario1))
})

describe('Exemplar react-test-renderer Snapshot test', () => {
Expand Down
39 changes: 39 additions & 0 deletions tests/utils/apiGatewayStub.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import apiGatewayStub from '../../utils/apiGatewayStub'
import { ScenarioType } from '../../utils/getScenarioContent'

describe('The API gateway stub response for the Pending Determination scenario', () => {
it('is correct', () => {
const response = apiGatewayStub(ScenarioType.Scenario1)
expect(response.pendingDetermination.length).toBeGreaterThan(0)
})
})

describe('The API gateway stub response for the Base States', () => {
it('is correct for Scenario 7', () => {
const response = apiGatewayStub(ScenarioType.Scenario7)
expect([null, [], false, undefined]).toContain(response.pendingDetermination)
expect(response.hasPendingWeeks).toBe(false)
expect(response.hasCertificationWeeksAvailable).toBe(false)
})

it('is correct for Scenario 8', () => {
const response = apiGatewayStub(ScenarioType.Scenario8)
expect([null, [], false, undefined]).toContain(response.pendingDetermination)
expect(response.hasPendingWeeks).toBe(false)
expect(response.hasCertificationWeeksAvailable).toBe(true)
})

it('is correct for Scenario 9', () => {
const response = apiGatewayStub(ScenarioType.Scenario9)
expect([null, [], false, undefined]).toContain(response.pendingDetermination)
expect(response.hasPendingWeeks).toBe(true)
expect(response.hasCertificationWeeksAvailable).toBe(false)
})

it('is correct for Scenario 10', () => {
const response = apiGatewayStub(ScenarioType.Scenario10)
expect([null, [], false, undefined]).toContain(response.pendingDetermination)
expect(response.hasPendingWeeks).toBe(true)
expect(response.hasCertificationWeeksAvailable).toBe(true)
})
})
114 changes: 46 additions & 68 deletions tests/utils/getScenarioContent.test.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,76 @@
import getScenarioContent, { getScenario, ScenarioType } from '../../utils/getScenarioContent'
import { ScenarioContent } from '../../types/common'

// Shared test constants for mock API gateway responses
const pendingDeterminationScenario = { pendingDetermination: ['temporary text'] }
const basePendingScenario = { hasPendingWeeks: true }
const baseNoPendingScenario = { hasPendingWeeks: false }
import { getClaimStatusDescription, getScenario, ScenarioType } from '../../utils/getScenarioContent'
import apiGatewayStub from '../../utils/apiGatewayStub'
import { getNumericEnumKeys } from '../../utils/numericEnum'

/**
* Begin tests
*/

// Test getScenarioContent()
describe('Retrieving the scenario content', () => {
it('returns the correct status description for the scenario', () => {
const pendingDetermination: ScenarioContent = getScenarioContent(pendingDeterminationScenario)
expect(pendingDetermination.statusContent.statusDescription).toBe('claim-status:pending-determination.description')

const basePending: ScenarioContent = getScenarioContent(basePendingScenario)
expect(basePending.statusContent.statusDescription).toBe('claim-status:base-pending.description')

const baseNoPending: ScenarioContent = getScenarioContent(baseNoPendingScenario)
expect(baseNoPending.statusContent.statusDescription).toBe('claim-status:base-no-pending.description')
// Test getClaimStatusDescripton()
describe('Getting the Claim Status description', () => {
it('returns the correct description for the scenario', () => {
for (const key of getNumericEnumKeys(ScenarioType)) {
expect(getClaimStatusDescription(key)).toEqual(
expect.stringMatching(/claim-status:scenarios.scenario[0-9]+.description/),
)
}
})
})

// Test getScenario(): pending determination scenario
describe('The pending determination scenario', () => {
/**
* Test getScenario()
*
* Refer to ScenarioType and ScenarioTypeNames for which scenario has which number.
*/

// Scenario 1
describe('Scenario 1', () => {
it('is returned when there is a pendingDetermination object', () => {
const scenarioType: ScenarioType = getScenario(pendingDeterminationScenario)
expect(scenarioType).toBe(ScenarioType.PendingDetermination)
const pendingDeterminationScenario = apiGatewayStub(ScenarioType.Scenario1)
const scenarioType = getScenario(pendingDeterminationScenario)
expect(scenarioType).toBe(ScenarioType.Scenario1)
})

it('is returned when there is a pendingDetermination object regardless of other criteria', () => {
const pendingDeterminationScenarioWith = {
pendingDetermination: ['temporary text'],
hasPendingWeeks: true,
}
const scenarioTypeWith: ScenarioType = getScenario(pendingDeterminationScenarioWith)
expect(scenarioTypeWith).toBe(ScenarioType.PendingDetermination)
const scenarioTypeWith = getScenario(pendingDeterminationScenarioWith)
expect(scenarioTypeWith).toBe(ScenarioType.Scenario1)

const pendingDeterminationScenarioWithout = {
pendingDetermination: ['temporary text'],
hasPendingWeeks: false,
}
const scenarioTypeWithout: ScenarioType = getScenario(pendingDeterminationScenarioWithout)
expect(scenarioTypeWithout).toBe(ScenarioType.PendingDetermination)
})
})

// Test getScenario(): base state with pending weeks scenario
describe('The base state (with pending weeks) scenario', () => {
it('is returned when there are pending weeks', () => {
const scenarioType: ScenarioType = getScenario(basePendingScenario)
expect(scenarioType).toBe(ScenarioType.BasePending)
const scenarioTypeWithout = getScenario(pendingDeterminationScenarioWithout)
expect(scenarioTypeWithout).toBe(ScenarioType.Scenario1)
})

it('is returned when there are pending weeks and pendingDetermination is null', () => {
const basePendingScenarioNull = { hasPendingWeeks: true, pendingDetermination: null }
const scenarioType: ScenarioType = getScenario(basePendingScenarioNull)
expect(scenarioType).toBe(ScenarioType.BasePending)
it('is not returned if pendingDetermination is null', () => {
const pendingDeterminationScenarioNull = { pendingDetermination: null }
const scenarioTypeNull = getScenario(pendingDeterminationScenarioNull)
expect(scenarioTypeNull).not.toBe(ScenarioType.Scenario1)
})

it('is returned when there are pending weeks and pendingDetermination is an empty array', () => {
const basePendingScenarioEmpty = { hasPendingWeeks: true, pendingDetermination: [] }
const scenarioType: ScenarioType = getScenario(basePendingScenarioEmpty)
expect(scenarioType).toBe(ScenarioType.BasePending)
it('is not returned if pendingDetermination is an empty array', () => {
const pendingDeterminationScenarioEmpty = { pendingDetermination: [] }
const scenarioTypeEmpty = getScenario(pendingDeterminationScenarioEmpty)
expect(scenarioTypeEmpty).not.toBe(ScenarioType.Scenario1)
})
})

// Test getScenario(): base state with no pending weeks scenario
describe('The base state (with no pending weeks) scenario', () => {
it('is returned when there are no pending weeks', () => {
const scenarioType: ScenarioType = getScenario(baseNoPendingScenario)
expect(scenarioType).toBe(ScenarioType.BaseNoPending)
})

it('is returned when there are no pending weeks and pendingDetermination is null', () => {
const baseNoPendingScenarioNull = { hasPendingWeeks: false, pendingDetermination: null }
const scenarioType: ScenarioType = getScenario(baseNoPendingScenarioNull)
expect(scenarioType).toBe(ScenarioType.BaseNoPending)
})

it('is returned when there are no pending weeks and pendingDetermination is an empty array', () => {
const baseNoPendingScenarioEmpty = { hasPendingWeeks: false, pendingDetermination: [] }
const scenarioType: ScenarioType = getScenario(baseNoPendingScenarioEmpty)
expect(scenarioType).toBe(ScenarioType.BaseNoPending)
})
})

// Test getScenario(): error
describe('Getting the scenario', () => {
it.skip('errors when given an unknown scenario', () => {
expect(() => {
getScenario({})
}).toThrowError('Unknown Scenario')
// Scenarios 7-10
describe('The Base State scenarios', () => {
it('are returned as expected', () => {
const baseScenarios = [
ScenarioType.Scenario7,
ScenarioType.Scenario8,
ScenarioType.Scenario9,
ScenarioType.Scenario10,
]
for (const scenarioType of baseScenarios) {
expect(getScenario(apiGatewayStub(scenarioType))).toBe(scenarioType)
}
})
})
19 changes: 19 additions & 0 deletions tests/utils/numericEnum.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getNumericEnumKeys, getNumericEnumLength } from '../../utils/numericEnum'

// Shared mock enum
enum TestEnum {
FOO,
BAR,
}

describe('Numeric enum length', () => {
it('equals the number of written elements', () => {
expect(getNumericEnumLength(TestEnum)).toEqual(2)
})
})

describe('Numeric enum keys', () => {
it('equals an array of incrementing numbers', () => {
expect(getNumericEnumKeys(TestEnum)).toEqual([0, 1])
})
})
5 changes: 4 additions & 1 deletion types/common.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Type aliases
export type I18nString = string

// Type interfaces for API gateway result
export interface PendingDetermination {
determinationStatus?: null | undefined | string
Expand All @@ -19,7 +22,7 @@ export interface Claim {
uniqueNumber?: null | string
claimDetails?: null | ClaimDetailsResult
hasPendingWeeks?: null | undefined | boolean
hasCertificationWeeksAvailable?: null | boolean
hasCertificationWeeksAvailable?: null | undefined | boolean
pendingDetermination?: null | [PendingDetermination]
}

Expand Down
24 changes: 19 additions & 5 deletions utils/apiGatewayStub.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/**
* Stub for the API gateway.
*
* Provides stub responses for API gateway queries for Storybook and Jest testing.
*/

import { ScenarioType } from '../utils/getScenarioContent'
import { Claim } from '../types/common'

/**
* Stub the API gateway response for a given scenario.
*/
export default function apiGatewayStub(scenarioType: ScenarioType): Claim {
console.log('apiGatewayStub')
const claim: Claim = {
uniqueNumber: null,
claimDetails: null,
Expand All @@ -15,16 +20,25 @@ export default function apiGatewayStub(scenarioType: ScenarioType): Claim {
}

switch (scenarioType) {
case ScenarioType.PendingDetermination:
case ScenarioType.Scenario1:
claim.pendingDetermination = [{ determinationStatus: null }]
break

case ScenarioType.BasePending:
// @TODO: This scenario should probably not be the default case.
// case ScenarioType.Scenario7:

case ScenarioType.Scenario8:
claim.hasCertificationWeeksAvailable = true
break

case ScenarioType.Scenario9:
claim.hasPendingWeeks = true
break

// @TODO: This scenario should probably not be the default case.
// case ScenarioType.BaseNoPending:
case ScenarioType.Scenario10:
claim.hasPendingWeeks = true
claim.hasCertificationWeeksAvailable = true
break

// @TODO: No match should throw an error
// default:
Expand Down
Loading

0 comments on commit 16c3ee3

Please sign in to comment.