diff --git a/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.test.ts b/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.test.ts index 43e66f8b0c..b959a118a4 100644 --- a/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.test.ts +++ b/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.test.ts @@ -1,7 +1,10 @@ import { HealthPlanPackageType } from '../../domain-models' import { toDomain } from '../../../../app-web/src/common-code/proto/healthPlanFormDataProto' import { sharedTestPrismaClient } from '../../testHelpers/storeHelpers' -import { insertHealthPlanPackage } from './insertHealthPlanPackage' +import { + insertHealthPlanPackage, + InsertHealthPlanPackageArgsType, +} from './insertHealthPlanPackage' import { isStoreError } from '../storeError' describe('insertHealthPlanPackage', () => { @@ -13,14 +16,14 @@ describe('insertHealthPlanPackage', () => { const client = await sharedTestPrismaClient() - const args = { + const args: InsertHealthPlanPackageArgsType = { stateCode: 'FL', + populationCovered: 'MEDICAID', programIDs: ['smmc'], riskBasedContract: false, - submissionType: 'CONTRACT_ONLY' as const, + submissionType: 'CONTRACT_ONLY', submissionDescription: 'concurrency state code test', - contractType: 'BASE' as const, - populationCovered: 'MEDICAID' as const, + contractType: 'BASE', } const resultPromises = [] diff --git a/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.ts b/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.ts index 7ec62631fc..d754887c1c 100644 --- a/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.ts +++ b/services/app-api/src/postgres/healthPlanPackage/insertHealthPlanPackage.ts @@ -18,7 +18,7 @@ import { PopulationCoveredType } from '../../gen/gqlServer' export type InsertHealthPlanPackageArgsType = { stateCode: string - populationCovered?: PopulationCoveredType + populationCovered: PopulationCoveredType programIDs: string[] riskBasedContract?: boolean submissionType: SubmissionType diff --git a/services/app-api/src/resolvers/configureResolvers.ts b/services/app-api/src/resolvers/configureResolvers.ts index cf9a6d4194..1f7f396fa6 100644 --- a/services/app-api/src/resolvers/configureResolvers.ts +++ b/services/app-api/src/resolvers/configureResolvers.ts @@ -57,8 +57,7 @@ export function configureResolvers( submitHealthPlanPackage: submitHealthPlanPackageResolver( store, emailer, - emailParameterStore, - launchDarkly + emailParameterStore ), unlockHealthPlanPackage: unlockHealthPlanPackageResolver( store, diff --git a/services/app-api/src/resolvers/healthPlanPackage/createHealthPlanPackage.test.ts b/services/app-api/src/resolvers/healthPlanPackage/createHealthPlanPackage.test.ts index 289fda662e..964adbd395 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/createHealthPlanPackage.test.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/createHealthPlanPackage.test.ts @@ -37,6 +37,7 @@ describe.each(flagValueTestParameters)( }) const input: CreateHealthPlanPackageInput = { + populationCovered: 'MEDICAID', programIDs: [ '5c10fe9f-bec9-416f-a20c-718b152ad633', '037af66b-81eb-4472-8b80-01edf17d12d9', @@ -74,6 +75,7 @@ describe.each(flagValueTestParameters)( ldService: mockLDService, }) const input: CreateHealthPlanPackageInput = { + populationCovered: 'MEDICAID', programIDs: ['xyz123'], riskBasedContract: false, submissionType: 'CONTRACT_ONLY', @@ -100,6 +102,7 @@ describe.each(flagValueTestParameters)( }) const input: CreateHealthPlanPackageInput = { + populationCovered: 'MEDICAID', programIDs: ['xyz123'], riskBasedContract: false, submissionType: 'CONTRACT_ONLY', diff --git a/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.test.ts b/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.test.ts index 568602375f..0dc70fd29b 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.test.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.test.ts @@ -23,7 +23,6 @@ import { getTestStateAnalystsEmails, } from '../../testHelpers/parameterStoreHelpers' import * as awsSESHelpers from '../../testHelpers/awsSESHelpers' -import { testLDService } from '../../testHelpers/launchDarklyHelpers' import { testCMSUser, testStateUser } from '../../testHelpers/userHelpers' describe('submitHealthPlanPackage', () => { @@ -903,10 +902,7 @@ describe('submitHealthPlanPackage', () => { describe('Feature flagged population coverage question test', () => { it('errors when population coverage question is undefined', async () => { - const mockLDService = testLDService({ 'chip-only-form': true }) - const server = await constructTestPostgresServer({ - ldService: mockLDService, - }) + const server = await constructTestPostgresServer() // setup const initialPkg = await createAndUpdateTestHealthPlanPackage(server, { diff --git a/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.ts b/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.ts index fa848da18c..a4c040030f 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/submitHealthPlanPackage.ts @@ -6,7 +6,6 @@ import { hasAnyValidRateData, isContractAndRates, removeRatesData, - hasValidPopulationCoverage, removeInvalidProvisionsAndAuthorities, isValidAndCurrentLockedHealthPlanFormData, hasValidSupportingDocumentCategories, @@ -29,9 +28,7 @@ import { } from '../attributeHelper' import { toDomain } from '../../../../app-web/src/common-code/proto/healthPlanFormDataProto' import { EmailParameterStore } from '../../parameterStore' -import { LDService } from '../../launchDarkly/launchDarkly' import { GraphQLError } from 'graphql' -import { FeatureFlagSettings } from 'app-web/src/common-code/featureFlags' import type { UnlockedHealthPlanFormDataType, @@ -70,8 +67,7 @@ export function isSubmissionError(err: unknown): err is SubmissionError { // This strategy (returning a different type from validation) is taken from the // "parse, don't validate" article: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ function submit( - draft: UnlockedHealthPlanFormDataType, - featureFlags?: FeatureFlagSettings + draft: UnlockedHealthPlanFormDataType ): LockedHealthPlanFormDataType | SubmissionError { const maybeStateSubmission: Record = { ...draft, @@ -79,20 +75,9 @@ function submit( submittedAt: new Date(), } - // move this check into isValidContract when feature flag is removed - const validPopulationCovered = featureFlags?.['chip-only-form'] - ? hasValidPopulationCoverage( - maybeStateSubmission as LockedHealthPlanFormDataType - ) - : true - - if ( - isValidAndCurrentLockedHealthPlanFormData(maybeStateSubmission) && - validPopulationCovered - ) + if (isValidAndCurrentLockedHealthPlanFormData(maybeStateSubmission)) return maybeStateSubmission else if ( - !validPopulationCovered || !hasValidContract(maybeStateSubmission as LockedHealthPlanFormDataType) ) { return { @@ -139,8 +124,7 @@ function submit( export function submitHealthPlanPackageResolver( store: Store, emailer: Emailer, - emailParameterStore: EmailParameterStore, - launchDarkly: LDService + emailParameterStore: EmailParameterStore ): MutationResolvers['submitHealthPlanPackage'] { return async (_parent, { input }, context) => { const { user, span } = context @@ -148,11 +132,6 @@ export function submitHealthPlanPackageResolver( setResolverDetailsOnActiveSpan('submitHealthPlanPackage', user, span) span?.setAttribute('mcreview.package_id', pkgID) - const chipOnlyFormFlag = await launchDarkly.getFeatureFlag( - context, - 'chip-only-form' - ) - // This resolver is only callable by state users if (!isStateUser(user)) { logError( @@ -284,9 +263,7 @@ export function submitHealthPlanPackageResolver( } // attempt to parse into a StateSubmission - const submissionResult = submit(draftResult, { - 'chip-only-form': chipOnlyFormFlag, - }) + const submissionResult = submit(draftResult) if (isSubmissionError(submissionResult)) { const errMessage = submissionResult.message diff --git a/services/app-api/src/testHelpers/gqlHelpers.ts b/services/app-api/src/testHelpers/gqlHelpers.ts index 14a8ce02a5..aaaefe5c61 100644 --- a/services/app-api/src/testHelpers/gqlHelpers.ts +++ b/services/app-api/src/testHelpers/gqlHelpers.ts @@ -132,8 +132,9 @@ const createTestHealthPlanPackage = async ( const programIDs = programs.map((program) => program.id) const input: CreateHealthPlanPackageInput = { programIDs: programIDs, + populationCovered: 'MEDICAID', riskBasedContract: false, - submissionType: 'CONTRACT_ONLY' as const, + submissionType: 'CONTRACT_ONLY', submissionDescription: 'A created submission', contractType: 'BASE', } diff --git a/services/app-graphql/src/schema.graphql b/services/app-graphql/src/schema.graphql index 0b774485a1..e61739561b 100644 --- a/services/app-graphql/src/schema.graphql +++ b/services/app-graphql/src/schema.graphql @@ -240,7 +240,7 @@ type Mutation { input CreateHealthPlanPackageInput { "Population that the contract covers" - populationCovered: PopulationCoveredType + populationCovered: PopulationCoveredType! "An array of managed care program IDs this package covers" programIDs: [ID!]! "Whether or not this contract is risk based" diff --git a/services/app-web/src/common-code/featureFlags/flags.ts b/services/app-web/src/common-code/featureFlags/flags.ts index 796390ff4b..d806ac7587 100644 --- a/services/app-web/src/common-code/featureFlags/flags.ts +++ b/services/app-web/src/common-code/featureFlags/flags.ts @@ -55,13 +55,6 @@ const featureFlags = { flag: 'packages-with-shared-rates', defaultValue: false, }, - /** - * Enables Chip-only form changes - */ - CHIP_ONLY_FORM: { - flag: 'chip-only-form', - defaultValue: false, - }, /** * Enables supporting documents to be associated with a specific rate certification on the Rate Details page */ diff --git a/services/app-web/src/common-code/healthPlanFormDataMocks/healthPlanFormData.ts b/services/app-web/src/common-code/healthPlanFormDataMocks/healthPlanFormData.ts index 16bafbf2ab..8d882269b0 100644 --- a/services/app-web/src/common-code/healthPlanFormDataMocks/healthPlanFormData.ts +++ b/services/app-web/src/common-code/healthPlanFormDataMocks/healthPlanFormData.ts @@ -46,6 +46,7 @@ function newHealthPlanFormData(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_AND_RATES', riskBasedContract: true, @@ -68,6 +69,7 @@ function basicHealthPlanFormData(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_AND_RATES', riskBasedContract: true, @@ -94,6 +96,7 @@ function contractOnly(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_ONLY', riskBasedContract: false, @@ -120,6 +123,7 @@ function contractAmendedOnly(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_ONLY', riskBasedContract: false, @@ -167,6 +171,7 @@ function unlockedWithContacts(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_AND_RATES', riskBasedContract: true, @@ -254,6 +259,7 @@ function unlockedWithDocuments(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_AND_RATES', riskBasedContract: true, @@ -364,6 +370,7 @@ function unlockedWithFullRates(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_AND_RATES', riskBasedContract: true, @@ -409,7 +416,7 @@ function unlockedWithFullRates(): UnlockedHealthPlanFormDataType { documentCategories: ['RATES'], }, ], - supportingDocuments: [], + supportingDocuments: [], actuaryContacts: [ { name: 'foo bar', @@ -468,6 +475,7 @@ function unlockedWithFullContracts(): UnlockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_AND_RATES', riskBasedContract: true, @@ -540,7 +548,7 @@ function unlockedWithFullContracts(): UnlockedHealthPlanFormDataType { documentCategories: ['RATES'], }, ], - supportingDocuments: [], + supportingDocuments: [], actuaryContacts: [ { name: 'foo bar', @@ -599,6 +607,7 @@ function unlockedWithALittleBitOfEverything(): UnlockedHealthPlanFormDataType { status: 'DRAFT', stateCode: 'MN', stateNumber: 5, + populationCovered: 'MEDICAID', programIDs: [ mockMNState().programs[0].id, mockMNState().programs[1].id, @@ -735,6 +744,7 @@ function basicLockedHealthPlanFormData(): LockedHealthPlanFormDataType { stateNumber: 5, id: 'test-abc-123', stateCode: 'MN', + populationCovered: 'MEDICAID', programIDs: [mockMNState().programs[0].id], submissionType: 'CONTRACT_ONLY', riskBasedContract: false, diff --git a/services/app-web/src/common-code/healthPlanFormDataType/healthPlanFormData.ts b/services/app-web/src/common-code/healthPlanFormDataType/healthPlanFormData.ts index 3a2c6d59f9..f1a5d0e6f7 100644 --- a/services/app-web/src/common-code/healthPlanFormDataType/healthPlanFormData.ts +++ b/services/app-web/src/common-code/healthPlanFormDataType/healthPlanFormData.ts @@ -71,7 +71,8 @@ const hasValidContract = (sub: LockedHealthPlanFormDataType): boolean => sub.managedCareEntities.length !== 0 && sub.federalAuthorities.length !== 0 && sub.riskBasedContract !== undefined && - hasValidModifiedProvisions(sub) + hasValidModifiedProvisions(sub) && + hasValidPopulationCoverage(sub) const hasValidPopulationCoverage = ( sub: LockedHealthPlanFormDataType @@ -305,7 +306,7 @@ const generateRateName = ( return rateName } - // This logic is no longer needed once SUPPORTING_DOCS_BY_RATE flag is on in production +// This logic is no longer needed once SUPPORTING_DOCS_BY_RATE flag is on in production const convertRateSupportingDocs = ( documents: SubmissionDocument[] ): SubmissionDocument[] => { diff --git a/services/app-web/src/common-code/proto/healthPlanFormDataProto/testData/unlockedWithALittleBitOfEverything-2023-07-17.proto b/services/app-web/src/common-code/proto/healthPlanFormDataProto/testData/unlockedWithALittleBitOfEverything-2023-07-17.proto new file mode 100644 index 0000000000..793bc6c428 Binary files /dev/null and b/services/app-web/src/common-code/proto/healthPlanFormDataProto/testData/unlockedWithALittleBitOfEverything-2023-07-17.proto differ diff --git a/services/app-web/src/components/FileUpload/FileUpload.stories.tsx b/services/app-web/src/components/FileUpload/FileUpload.stories.tsx index 8b6b9434ff..80a2029de3 100644 --- a/services/app-web/src/components/FileUpload/FileUpload.stories.tsx +++ b/services/app-web/src/components/FileUpload/FileUpload.stories.tsx @@ -26,7 +26,7 @@ export const DemoListUploadSuccess = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -50,7 +50,7 @@ export const DemoTableUploadSuccess = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -74,7 +74,7 @@ export const DemoListUploadFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -98,7 +98,7 @@ export const DemoTableUploadFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -122,7 +122,7 @@ export const DemoListScanFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -146,7 +146,7 @@ export const DemoTableScanFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) diff --git a/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx b/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx index ed5c30386b..3c4c55d1f8 100644 --- a/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx +++ b/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx @@ -10,7 +10,7 @@ export default { const Template: Story = (args) => ( console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx b/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx index b9479cc2ba..b239477964 100644 --- a/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx +++ b/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx @@ -16,7 +16,7 @@ const Template: Story = (args) => ( initialValues={{ program: '' }} validationSchema={schema} validateOnMount={true} - onSubmit={(e) => console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx b/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx index efa171c147..3b0023cfa9 100644 --- a/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx +++ b/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx @@ -10,7 +10,7 @@ export default { const Template: Story = (args) => ( console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx b/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx index 2c5d4d0716..7f3cef6d73 100644 --- a/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx +++ b/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx @@ -18,7 +18,7 @@ const Template: Story = (args) => ( initialValues={{ submissionDescription: '' }} validationSchema={schema} validateOnMount={true} - onSubmit={(e) => console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx b/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx index 5ce3417bcb..f73ed76a73 100644 --- a/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx +++ b/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx @@ -19,7 +19,7 @@ const Template: Story = (args) => ( initialValues={{ submissionDescription: '' }} validationSchema={schema} validateOnMount={true} - onSubmit={(e) => console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx index 34899bc55f..7a7a67bbf3 100644 --- a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx @@ -31,6 +31,7 @@ describe('ContractDetailsSummarySection', () => { renderWithProviders( { it('can render state submission on summary page without errors (submission summary behavior)', () => { renderWithProviders( { it('can render all contract details fields', () => { renderWithProviders( { it('displays correct effective dates text for base contract', () => { renderWithProviders( @@ -137,6 +141,7 @@ describe('ContractDetailsSummarySection', () => { it('displays correct effective dates text for contract amendment', () => { renderWithProviders( @@ -179,6 +184,7 @@ describe('ContractDetailsSummarySection', () => { } renderWithProviders( @@ -220,6 +226,7 @@ describe('ContractDetailsSummarySection', () => { it('does not render supporting contract documents table when no documents exist', () => { renderWithProviders( @@ -235,6 +242,7 @@ describe('ContractDetailsSummarySection', () => { it('does not render download all button when on previous submission', () => { renderWithProviders( @@ -249,6 +257,7 @@ describe('ContractDetailsSummarySection', () => { it('renders federal authorities for a medicaid contract', async () => { renderWithProviders( { it('renders federal authorities for a CHIP contract as expected, removing invalid authorities', async () => { renderWithProviders( { it('renders provisions and MLR references for a medicaid amendment', () => { renderWithProviders( @@ -354,9 +367,7 @@ describe('ContractDetailsSummarySection', () => { ).toBeInTheDocument() expect( - within(modifiedProvisions).getByText( - /Risk-sharing strategy/ - ) + within(modifiedProvisions).getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( within(modifiedProvisions).getByText( @@ -425,6 +436,9 @@ describe('ContractDetailsSummarySection', () => { it('renders provisions and MLR references for a medicaid base contract', () => { renderWithProviders( { ).toBeInTheDocument() expect( - within(modifiedProvisions).getByText( - /Risk-sharing strategy/ - ) + within(modifiedProvisions).getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( within(modifiedProvisions).getByText( @@ -475,6 +487,9 @@ describe('ContractDetailsSummarySection', () => { it('renders provisions with correct MLR references for CHIP amendment', () => { renderWithProviders( { } renderWithProviders( { } renderWithProviders( { } renderWithProviders( , @@ -701,6 +725,9 @@ describe('ContractDetailsSummarySection', () => { } renderWithProviders( { } renderWithProviders( , diff --git a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx index 2fb4b41ca6..451c242124 100644 --- a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react' import { DataDetail } from '../../../components/DataDetail' import { SectionHeader } from '../../../components/SectionHeader' import { UploadedDocumentsTable } from '../../../components/SubmissionSummarySection' -import { DocumentDateLookupTable } from '../../../pages/SubmissionSummary/SubmissionSummary' import { ContractExecutionStatusRecord, FederalAuthorityRecord, @@ -30,11 +29,12 @@ import { HealthPlanFormDataType, federalAuthorityKeysForCHIP, } from '../../../common-code/healthPlanFormDataType' +import { DocumentDateLookupTableType } from '../../../documentHelpers/makeDocumentDateLookupTable' export type ContractDetailsSummarySectionProps = { submission: HealthPlanFormDataType navigateTo?: string - documentDateLookupTable?: DocumentDateLookupTable + documentDateLookupTable: DocumentDateLookupTableType isCMSUser?: boolean submissionName: string } @@ -168,7 +168,11 @@ export const ContractDetailsSummarySection = ({ )} @@ -184,7 +188,11 @@ export const ContractDetailsSummarySection = ({ { it('can render draft submission without errors', () => { renderWithProviders( { it('can render state submission without errors', () => { renderWithProviders( { it('can render all rate details fields for amendment to prior rate certification submission', () => { renderWithProviders( { const statePrograms = mockMNState().programs renderWithProviders( { renderWithProviders( { 'MCR-MN-0005-SNBC-RATE-20221014-20221014-CERTIFICATION-20221014' renderWithProviders( { } renderWithProviders( { it('does not render supporting rate documents when they do not exist', () => { renderWithProviders( { it('does not render download all button when on previous submission', () => { renderWithProviders( { it('renders rate cell capitation type', () => { renderWithProviders( { draftSubmission.rateInfos[0].rateCapitationType = 'RATE_RANGE' renderWithProviders( { ] renderWithProviders( { ] renderWithProviders( { draftSubmission.rateInfos = mockRateInfos renderWithProviders( { renderWithProviders( { draftSubmission.rateInfos = mockRateInfos renderWithProviders( { draftSubmission.rateInfos = mockRateInfos renderWithProviders( { ] renderWithProviders( { ] renderWithProviders( { } renderWithProviders( { it('renders submitted package without errors', () => { renderWithProviders( ) : ( 'LOADING...' @@ -357,7 +357,6 @@ export const RateDetailsSummarySection = ({ documentDateLookupTable={ documentDateLookupTable } - isCMSUser={isCMSUser} caption="Rate supporting documents" isSupportingDocuments documentCategory="Rate-supporting" @@ -378,7 +377,6 @@ export const RateDetailsSummarySection = ({ { }) it('renders expected fields for draft package on review and submit', () => { - ldUseClientSpy({ 'chip-only-form': true }) renderWithProviders( { }) it('renders missing field message for population coverage question when expected', () => { - ldUseClientSpy({ 'chip-only-form': true }) renderWithProviders( p.name) const isSubmitted = submission.status === 'SUBMITTED' - const ldClient = useLDClient() - const showCHIPOnlyForm = ldClient?.variation( - featureFlags.CHIP_ONLY_FORM.flag, - featureFlags.CHIP_ONLY_FORM.defaultValue - ) - return (
- {showCHIPOnlyForm && ( - - )} + diff --git a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.test.tsx b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.test.tsx index fb9e950cf0..bea8133808 100644 --- a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.test.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.test.tsx @@ -1,12 +1,17 @@ -import { screen, waitFor } from '@testing-library/react' +import { screen, waitFor, within } from '@testing-library/react' import { renderWithProviders } from '../../../testHelpers/jestHelpers' import { UploadedDocumentsTable } from './UploadedDocumentsTable' import { fetchCurrentUserMock, mockValidCMSUser, } from '../../../testHelpers/apolloMocks' +import { DocumentDateLookupTableType } from '../../../documentHelpers/makeDocumentDateLookupTable' +import { SubmissionDocument } from '../../../common-code/healthPlanFormDataType' describe('UploadedDocumentsTable', () => { + const emptyDocumentsTable = () => { + return { previousSubmissionDate: '01/01/01' } + } it('renders documents without errors', async () => { const testDocuments = [ { @@ -17,6 +22,7 @@ describe('UploadedDocumentsTable', () => { ] renderWithProviders( { renderWithProviders( { ) renderWithProviders( { ).toBeNull() }) }) - it('renders date added when supplied with a date lookup table', async () => { - const testDocuments = [ + it('renders date added when supplied with a date lookup table and is CMS user viewing submission', async () => { + const testDocuments: SubmissionDocument[] = [ { s3URL: 's3://foo/bar/test-1', name: 'supporting docs test 1', @@ -167,7 +175,67 @@ describe('UploadedDocumentsTable', () => { ], }, ] - const dateLookupTable = { + const dateLookupTable: DocumentDateLookupTableType = { + 's3://foo/bar/test-1': + 'Fri Mar 25 2022 16:13:20 GMT-0500 (Central Daylight Time)', + 's3://foo/bar/test-2': + 'Sat Mar 26 2022 16:13:20 GMT-0500 (Central Daylight Time)', + 's3://foo/bar/test-3': + 'Sun Mar 27 2022 16:13:20 GMT-0500 (Central Daylight Time)', + previousSubmissionDate: + 'Sun Mar 26 2022 16:13:20 GMT-0500 (Central Daylight Time)', + } + renderWithProviders( + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + ], + }, + } + ) + await waitFor(() => { + const rows = screen.getAllByRole('row') + expect(rows).toHaveLength(4) + expect(rows[0]).toHaveTextContent('Date added') + expect(rows[1]).toHaveTextContent('3/25/22') + expect(rows[2]).toHaveTextContent('3/26/22') + expect(rows[3]).toHaveTextContent('3/27/22') + }) + }) + + it('renders date added when supplied with a date lookup table and is State user', async () => { + const testDocuments: SubmissionDocument[] = [ + { + s3URL: 's3://foo/bar/test-1', + name: 'supporting docs test 1', + documentCategories: ['CONTRACT_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-2', + name: 'supporting docs test 2', + documentCategories: ['RATES_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-3', + name: 'supporting docs test 3', + documentCategories: [ + 'CONTRACT_RELATED' as const, + 'RATES_RELATED' as const, + ], + }, + ] + const dateLookupTable: DocumentDateLookupTableType = { 's3://foo/bar/test-1': 'Fri Mar 25 2022 16:13:20 GMT-0500 (Central Daylight Time)', 's3://foo/bar/test-2': @@ -184,7 +252,6 @@ describe('UploadedDocumentsTable', () => { documentCategory="Contract-supporting" isSupportingDocuments documentDateLookupTable={dateLookupTable} - isCMSUser={true} />, { apolloProvider: { @@ -206,8 +273,69 @@ describe('UploadedDocumentsTable', () => { expect(rows[3]).toHaveTextContent('3/27/22') }) }) + + it('does not render a date added when supplied with empty date lookup table (this happens with new draft submissions)', async () => { + const testDocuments: SubmissionDocument[] = [ + { + s3URL: 's3://foo/bar/test-1', + name: 'supporting docs test 1', + documentCategories: ['CONTRACT_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-2', + name: 'supporting docs test 2', + documentCategories: ['RATES_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-3', + name: 'supporting docs test 3', + documentCategories: [ + 'CONTRACT_RELATED' as const, + 'RATES_RELATED' as const, + ], + }, + ] + const dateLookupTable: DocumentDateLookupTableType = { + previousSubmissionDate: null, + } + renderWithProviders( + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + ], + }, + } + ) + await waitFor(() => { + expect(screen.getByRole('table')).toBeInTheDocument() + // we have a table with rows for each document + const rows = screen.getAllByRole('row') + rows.shift() // get ride of column header row + expect(rows).toHaveLength(testDocuments.length) + + // There is a screenreader only "N/A" for each row + rows.forEach((row) => { + expect(within(row).getByText('N/A')).toBeInTheDocument() + expect(within(row).getByText('N/A')).toHaveAttribute( + 'class', + 'srOnly' + ) + }) + }) + }) it('shows the NEW tag when a document is submitted after the last submission', async () => { - const testDocuments = [ + const testDocuments: SubmissionDocument[] = [ { s3URL: 's3://foo/bar/test-1', name: 'supporting docs test 1', @@ -227,7 +355,7 @@ describe('UploadedDocumentsTable', () => { ], }, ] - const dateLookupTable = { + const dateLookupTable: DocumentDateLookupTableType = { 's3://foo/bar/test-1': 'Fri Mar 25 2022 16:13:20 GMT-0500 (Central Daylight Time)', 's3://foo/bar/test-2': @@ -244,7 +372,6 @@ describe('UploadedDocumentsTable', () => { documentCategory="Contract-supporting" isSupportingDocuments documentDateLookupTable={dateLookupTable} - isCMSUser={true} />, { apolloProvider: { @@ -299,7 +426,6 @@ describe('UploadedDocumentsTable', () => { documentCategory="Contract-supporting" isSupportingDocuments documentDateLookupTable={dateLookupTable} - isCMSUser={false} />, { apolloProvider: { diff --git a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx index 9dfa9ab29a..4ab2f3de31 100644 --- a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx @@ -3,96 +3,72 @@ import { Link } from '@trussworks/react-uswds' import { NavLink } from 'react-router-dom' import dayjs from 'dayjs' import { SubmissionDocument } from '../../../common-code/healthPlanFormDataType' -import { DocumentDateLookupTable } from '../../../pages/SubmissionSummary/SubmissionSummary' import styles from './UploadedDocumentsTable.module.scss' import { usePreviousSubmission } from '../../../hooks' import { SharedRateCertDisplay } from '../../../common-code/healthPlanFormDataType/UnlockedHealthPlanFormDataType' import { DocumentTag } from './DocumentTag' import { useDocument } from '../../../hooks/useDocument' -import { getDocumentKey } from '../../../documentHelpers/getDocumentKey' +import { DocumentDateLookupTableType } from '../../../documentHelpers/makeDocumentDateLookupTable' +import { getDocumentKey } from '../../../documentHelpers' +import { useAuth } from '../../../contexts/AuthContext' + export type UploadedDocumentsTableProps = { documents: SubmissionDocument[] caption: string | null - documentDateLookupTable?: DocumentDateLookupTable packagesWithSharedRateCerts?: SharedRateCertDisplay[] + documentDateLookupTable: DocumentDateLookupTableType isSupportingDocuments?: boolean documentCategory?: string // if this prop is not included, do not show category column - isEditing?: boolean - isCMSUser?: boolean -} - -type DocumentWithLink = { url: string | null } & SubmissionDocument - -const isBothContractAndRateSupporting = (doc: SubmissionDocument) => - doc.documentCategories.includes('CONTRACT_RELATED') && - doc.documentCategories.includes('RATES_RELATED') - -type LinkedPackagesListProps = { - unlinkDrafts: boolean - packages: SharedRateCertDisplay[] -} - -const linkedPackagesList = ({ - unlinkDrafts, - packages, -}: LinkedPackagesListProps): React.ReactElement[] => { - return packages.map((item, index) => { - const maybeComma = index > 0 ? ', ' : '' - const linkedPackageIsDraft = - item.packageName && item.packageName.includes('(Draft)') - - if (linkedPackageIsDraft && unlinkDrafts) { - return ( - - {maybeComma} - {item.packageName} - - ) - } else { - return ( - - {maybeComma} - - {item.packageName} - - - ) - } - }) + isEditing?: boolean // by default assume we are on summary page, if true, assume review and submit page } export const UploadedDocumentsTable = ({ documents, caption, documentCategory, - documentDateLookupTable, packagesWithSharedRateCerts, isSupportingDocuments = false, + documentDateLookupTable, isEditing = false, - isCMSUser, }: UploadedDocumentsTableProps): React.ReactElement => { const initialDocState = documents.map((doc) => ({ ...doc, url: null, + s3Key: null, })) - const { getDocumentsUrl } = useDocument() + const { loggedInUser } = useAuth() + const isCMSUser = loggedInUser?.__typename === 'CMSUser' + const { getDocumentsWithS3KeyAndUrl } = useDocument() const [refreshedDocs, setRefreshedDocs] = - useState(initialDocState) + useState(initialDocState) const shouldShowEditButton = isEditing && isSupportingDocuments const shouldShowAsteriskExplainer = refreshedDocs.some((doc) => isBothContractAndRateSupporting(doc) ) - const shouldHaveNewTag = (doc: SubmissionDocument) => { - const documentKey = getDocumentKey(doc) - return ( - isCMSUser && - documentDateLookupTable && - documentDateLookupTable[documentKey] > - documentDateLookupTable.previousSubmissionDate - ) + // canDisplayDateAddedForDocument - guards against passing in null or undefined to dayjs + // dates will be undefined in lookup table we are dealing with a new initial submission + const canDisplayDateAddedForDocument = (doc: DocumentWithS3Data) => { + const documentLookupKey = getDocumentKey(doc) + return documentLookupKey && documentDateLookupTable[documentLookupKey] + } + + const shouldHaveNewTag = (doc: DocumentWithS3Data) => { + const documentLookupKey = getDocumentKey(doc) + if (!isCMSUser) { + return false // design requirement, don't show new tag to state users on review submit + } + + if (!documentDateLookupTable || !doc || !doc.s3Key) { + return false // this is a document with bad s3 data + } + const documentDate = documentDateLookupTable?.[documentLookupKey] + const previousSubmissionDate = + documentDateLookupTable.previousSubmissionDate + + if (!documentDate || !previousSubmissionDate) { + return false // this document is on an initial submission or not submitted yet + } + return documentDate > previousSubmissionDate } const hasSharedRateCert = @@ -124,17 +100,18 @@ export const UploadedDocumentsTable = ({ useEffect(() => { const refreshDocuments = async () => { - const newDocuments = (await getDocumentsUrl( + const newDocuments = (await getDocumentsWithS3KeyAndUrl( documents, 'HEALTH_PLAN_DOCS' - )) as DocumentWithLink[] + )) as DocumentWithS3Data[] if (newDocuments.length) { setRefreshedDocs(newDocuments) } } void refreshDocuments() - }, [documents, getDocumentsUrl]) + }, [documents, getDocumentsWithS3KeyAndUrl]) + // Empty State if (refreshedDocs.length === 0) { return ( @@ -175,7 +152,7 @@ export const UploadedDocumentsTable = ({ {refreshedDocs.map((doc) => ( - {doc.url ? ( + {doc.url && doc.s3Key ? ( )} - {documentDateLookupTable - ? dayjs( - documentDateLookupTable[ - getDocumentKey(doc) - ] - ).format('M/D/YY') - : ''} + {canDisplayDateAddedForDocument(doc) ? ( + dayjs( + documentDateLookupTable[ + getDocumentKey(doc) + ] + ).format('M/D/YY') + ) : ( + N/A + )} {documentCategory && {documentCategory}} {showSharedInfo @@ -234,3 +213,50 @@ export const UploadedDocumentsTable = ({ ) } + +// TODO - get the api to return documents in this state rather than frontend generating on demand +type DocumentWithS3Data = { + url: string | null + s3Key: string | null +} & SubmissionDocument + +const isBothContractAndRateSupporting = (doc: SubmissionDocument) => + doc.documentCategories.includes('CONTRACT_RELATED') && + doc.documentCategories.includes('RATES_RELATED') + +type LinkedPackagesListProps = { + unlinkDrafts: boolean + packages: SharedRateCertDisplay[] +} + +const linkedPackagesList = ({ + unlinkDrafts, + packages, +}: LinkedPackagesListProps): React.ReactElement[] => { + return packages.map((item, index) => { + const maybeComma = index > 0 ? ', ' : '' + const linkedPackageIsDraft = + item.packageName && item.packageName.includes('(Draft)') + + if (linkedPackageIsDraft && unlinkDrafts) { + return ( + + {maybeComma} + {item.packageName} + + ) + } else { + return ( + + {maybeComma} + + {item.packageName} + + + ) + } + }) +} diff --git a/services/app-web/src/documentHelpers/getAllDocuments.ts b/services/app-web/src/documentHelpers/getAllDocuments.ts new file mode 100644 index 0000000000..cb7caefc43 --- /dev/null +++ b/services/app-web/src/documentHelpers/getAllDocuments.ts @@ -0,0 +1,14 @@ +import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' + +const getAllDocuments = (pkg: HealthPlanFormDataType) => { + let allDocuments = [...pkg.contractDocuments, ...pkg.documents] + if (pkg.rateInfos.length > 0) { + pkg.rateInfos.forEach((rateInfo) => { + allDocuments = allDocuments.concat(rateInfo.rateDocuments) + allDocuments = allDocuments.concat(rateInfo.supportingDocuments) + }) + } + return allDocuments +} + +export { getAllDocuments } diff --git a/services/app-web/src/documentHelpers/getDocumentKey.ts b/services/app-web/src/documentHelpers/getDocumentKey.ts index 04a4836505..5284ed803b 100644 --- a/services/app-web/src/documentHelpers/getDocumentKey.ts +++ b/services/app-web/src/documentHelpers/getDocumentKey.ts @@ -1,5 +1,9 @@ import { SubmissionDocument } from '../common-code/healthPlanFormDataType' -export const getDocumentKey = (doc: SubmissionDocument): string => { +// getDocumentKey - Returns a unique identifier for the document. This should be the document sha. +// Not to be confused with "s3Key" which is a different field / use case +const getDocumentKey = (doc: SubmissionDocument): string => { return doc.sha256 ? doc.sha256 : doc.s3URL } + +export { getDocumentKey } diff --git a/services/app-web/src/documentHelpers/index.ts b/services/app-web/src/documentHelpers/index.ts new file mode 100644 index 0000000000..cf7dde5e8a --- /dev/null +++ b/services/app-web/src/documentHelpers/index.ts @@ -0,0 +1,3 @@ +export { makeDocumentDateTable } from './makeDocumentDateLookupTable' +export { makeDocumentS3KeyLookup } from './makeDocumentKeyLookupList' +export { getDocumentKey } from './getDocumentKey' diff --git a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts index 7d771376cf..396b30b040 100644 --- a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts +++ b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts @@ -1,56 +1,75 @@ -import { makeDateTable } from './makeDocumentDateLookupTable' -import { mockSubmittedHealthPlanPackageWithRevision } from '../testHelpers/apolloMocks' +import { makeDocumentDateTable } from './makeDocumentDateLookupTable' +import { + mockDraftHealthPlanPackage, + mockSubmittedHealthPlanPackageWithRevision, +} from '../testHelpers/apolloMocks' import { UnlockedHealthPlanFormDataType } from '../common-code/healthPlanFormDataType' import { TextEncoder, TextDecoder } from 'util' +import { buildRevisionsLookup } from '../gqlHelpers/fetchHealthPlanPackageWrapper' Object.assign(global, { TextDecoder, TextEncoder }) -describe('makeDateTable', () => { +describe('makeDocumentDateTable', () => { it('should make a proper lookup table', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ - currentSubmissionData: { + const submission = mockSubmittedHealthPlanPackageWithRevision({ + currentSubmitInfo: { updatedAt: new Date('2022-03-28T17:56:32.953Z'), }, - previousSubmissionData: { + previousSubmitInfo: { updatedAt: new Date('2022-03-25T21:14:43.057Z'), }, - initialSubmissionData: { + initialSubmitInfo: { updatedAt: new Date('2022-03-25T21:13:20.420Z'), }, }) - const lookupTable = makeDateTable(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentDateTable(revisionsLookup) + expect(lookupTable).toEqual({ + 's3://bucketname/1648242632157-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648490162641-lifeofgalileo.pdf/lifeofgalileo.pdf': + new Date('2022-03-28T17:56:32.953Z'), + 's3://bucketname/1648242665634-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648242711421-Amerigroup Texas Inc copy.pdf/Amerigroup Texas Inc copy.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf': + new Date('2022-03-25T21:13:20.420Z'), + previousSubmissionDate: new Date('2022-03-25T21:14:43.057Z'), + }) + }) + + it('should return no document dates for submission still in initial draft', () => { + const submission = mockDraftHealthPlanPackage() + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentDateTable(revisionsLookup) - expect(JSON.stringify(lookupTable)).toEqual( - JSON.stringify({ - 's3://bucketname/1648242632157-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648490162641-lifeofgalileo.pdf/lifeofgalileo.pdf': - '2022-03-28T17:56:32.953Z', - 's3://bucketname/1648242665634-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648242711421-Amerigroup Texas Inc copy.pdf/Amerigroup Texas Inc copy.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf': - '2022-03-25T21:13:20.420Z', - previousSubmissionDate: '2022-03-25T21:14:43.057Z', - }) - ) + expect(lookupTable).toEqual({ + previousSubmissionDate: null, + }) }) - it('should use earliest document added date', () => { + + it('should use earliest document added date based that revisions submit date', () => { const docs: Partial = { documents: [ { - s3URL: 's3://bucketname/testDateDoc/testDateDoc.png', + s3URL: 's3://bucketname/testDateDoc/testDateDoc.pdf', name: 'Test Date Doc', documentCategories: ['CONTRACT_RELATED'], }, ], contractDocuments: [ { - s3URL: 's3://bucketname/key/foo.png', - name: 'contract doc', + s3URL: 's3://bucketname/key/replaced-contract.pdf', + name: 'replaced contract', documentCategories: ['CONTRACT'], }, ], @@ -63,28 +82,50 @@ describe('makeDateTable', () => { }, ], } - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + const submission = mockSubmittedHealthPlanPackageWithRevision({ currentSubmissionData: { ...docs, + }, + currentSubmitInfo: { updatedAt: new Date('2022-03-10T00:00:00.000Z'), }, previousSubmissionData: { ...docs, + }, + previousSubmitInfo: { updatedAt: new Date('2022-02-10T00:00:00.000Z'), }, initialSubmissionData: { ...docs, + contractDocuments: [ + { + s3URL: 's3://bucketname/key/original-contract.pdf', + name: 'original contract', + documentCategories: ['CONTRACT'], + }, + ], + }, + initialSubmitInfo: { updatedAt: new Date('2022-01-10T00:00:00.000Z'), }, }) - const lookupTable = makeDateTable(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentDateTable(revisionsLookup) expect(lookupTable).toEqual({ - 's3://bucketname/key/foo.png': new Date('2022-01-10T00:00:00.000Z'), - 's3://bucketname/testDateDoc/testDateDoc.png': new Date( + 's3://bucketname/key/original-contract.pdf': new Date( '2022-01-10T00:00:00.000Z' ), + 's3://bucketname/key/replaced-contract.pdf': new Date( + '2022-02-10T00:00:00.000Z' + ), + 's3://bucketname/testDateDoc/testDateDoc.pdf': new Date( + '2022-01-10T00:00:00.00' + ), previousSubmissionDate: new Date('2022-02-10T00:00:00.000Z'), }) }) diff --git a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts index 8109739861..49409e30ab 100644 --- a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts +++ b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts @@ -1,62 +1,53 @@ -import { base64ToDomain } from '../common-code/proto/healthPlanFormDataProto' -import { HealthPlanPackage } from '../gen/gqlClient' -import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' -import { DocumentDateLookupTable } from '../pages/SubmissionSummary/SubmissionSummary' -import { recordJSException } from '../otelHelpers/tracingHelper' +import { + ExpandedRevisionsType, + RevisionsLookupType, +} from '../gqlHelpers/fetchHealthPlanPackageWrapper' +import { getAllDocuments } from './getAllDocuments' import { getDocumentKey } from './getDocumentKey' -const DocBuckets = ['contractDocuments', 'rateDocuments', 'documents'] as const - -export function makeDateTableFromFormData( - formDatas: HealthPlanFormDataType[] -): DocumentDateLookupTable { - const lookupTable: DocumentDateLookupTable = {} +// DocumentDateLookupTableType - { document lookup key string : date string for "date added" } +type DocumentDateLookupTableType = { + previousSubmissionDate: string | null + [key: string]: string | null +} - formDatas.forEach((revisionData, index) => { - if (index === 1) { - // second most recent revision - lookupTable['previousSubmissionDate'] = revisionData.updatedAt - } - DocBuckets.forEach((bucket) => { - if (bucket === 'rateDocuments') { - revisionData.rateInfos.forEach((rateInfo) => { - rateInfo.rateDocuments.forEach((doc) => { - const documentKey = getDocumentKey(doc) - lookupTable[documentKey] = revisionData.updatedAt - }) - rateInfo.supportingDocuments.forEach((doc) => { - const documentKey = getDocumentKey(doc) - lookupTable[documentKey] = revisionData.updatedAt - }) - }) - } else { - revisionData[bucket].forEach((doc) => { - const documentKey = getDocumentKey(doc) - lookupTable[documentKey] = revisionData.updatedAt - }) +// getDateAdded - picks out the submit info updatedAt date for a revision +// value is undefined if document not yet submitted +const getDateAdded = ( + revisionData: ExpandedRevisionsType +): string | undefined => { + return revisionData.submitInfo?.updatedAt +} +// makeDateTable - generates unique document keys and their "date added" +// used for date added column on UploadedDocumentsTable displayed in SubmissionSummary and ReviewSubmit +// documents without a submitted date are excluded from list +// logic for unique document keys comes from getDocumentKey - This can be simplified once we have doc.sha everywhere +function makeDocumentDateTable( + revisionsLookup: RevisionsLookupType +): DocumentDateLookupTableType { + const lookupTable: DocumentDateLookupTableType = { + previousSubmissionDate: null, + } + Object.keys(revisionsLookup).forEach( + (revisionId: string, index: number) => { + const revision = revisionsLookup[revisionId] + if (index === 1) { + // second most recent revision + const previousSubmission = getDateAdded(revision) // used in UploadedDocumentsTable to determine if we should show NEW tag + if (previousSubmission) + lookupTable['previousSubmissionDate'] = previousSubmission } - }) - }) + const allDocuments = getAllDocuments(revision.formData) + allDocuments.forEach((doc) => { + const documentKey = getDocumentKey(doc) + const dateAdded = getDateAdded(revision) + if (dateAdded) lookupTable[documentKey] = dateAdded + }) + } + ) return lookupTable } -export const makeDateTable = ( - submissions: HealthPlanPackage -): DocumentDateLookupTable | undefined => { - const formDatas: HealthPlanFormDataType[] = [] - for (const revision of submissions.revisions) { - const revisionData = base64ToDomain(revision.node.formDataProto) - - if (revisionData instanceof Error) { - recordJSException( - `makeDocumentLookupTable: failed to read submission data; unable to display document dates. ID: ${submissions.id} Error message: ${revisionData.message}` - ) - // We are just ignoring this error in this code path. - } else { - formDatas.push(revisionData) - } - } - - return makeDateTableFromFormData(formDatas) -} +export { makeDocumentDateTable } +export type { DocumentDateLookupTableType } diff --git a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts index fc56e398ca..8f56015146 100644 --- a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts +++ b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts @@ -1,8 +1,9 @@ -import { makeDocumentList } from './makeDocumentKeyLookupList' +import { makeDocumentS3KeyLookup } from './makeDocumentKeyLookupList' import { mockSubmittedHealthPlanPackageWithRevision } from '../testHelpers/apolloMocks' import { UnlockedHealthPlanFormDataType } from '../common-code/healthPlanFormDataType' +import { buildRevisionsLookup } from '../gqlHelpers/fetchHealthPlanPackageWrapper' -describe('makeDocumentList', () => { +describe('makeDocumentS3KeyLookup', () => { const noSubmissionDocuments: Partial = { contractDocuments: [], rateInfos: [ @@ -17,47 +18,45 @@ describe('makeDocumentList', () => { } it('should make two lists with document s3 keys', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({}) - const lookupTable = makeDocumentList(submissions) - - expect(lookupTable).toEqual({ - currentDocuments: [ + const submission = mockSubmittedHealthPlanPackageWithRevision({}) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) + expect(lookupTable.currentDocuments.sort()).toEqual( + [ '1648242632157-Amerigroup Texas, Inc.pdf', '1648490162641-lifeofgalileo.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - ], - previousDocuments: [ - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', + ].sort() + ) + + expect(lookupTable.previousDocuments.sort()).toEqual( + [ '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', '1648242632157-Amerigroup Texas, Inc.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - ], - }) + ].sort() + ) }) + it('should return empty arrays for no documents in submission', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + const submission = mockSubmittedHealthPlanPackageWithRevision({ currentSubmissionData: noSubmissionDocuments, previousSubmissionData: noSubmissionDocuments, initialSubmissionData: noSubmissionDocuments, }) - const lookupTable = makeDocumentList(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) expect(lookupTable).toEqual({ currentDocuments: [], @@ -65,66 +64,50 @@ describe('makeDocumentList', () => { }) }) - it('should return empty array for currentDocuments', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + it('should return empty array for currentDocuments when none exist', () => { + const submission = mockSubmittedHealthPlanPackageWithRevision({ currentSubmissionData: noSubmissionDocuments, }) - const lookupTable = makeDocumentList(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) - expect(lookupTable).toEqual({ - currentDocuments: [], - previousDocuments: [ - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', + expect(lookupTable.currentDocuments).toEqual([]) + expect(lookupTable.previousDocuments.sort()).toEqual( + [ '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', '1648242632157-Amerigroup Texas, Inc.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - ], - }) + ].sort() + ) }) - it('should return empty array for previousDocuments', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + it('should return empty array for previousDocuments when none exist', () => { + const submission = mockSubmittedHealthPlanPackageWithRevision({ previousSubmissionData: noSubmissionDocuments, initialSubmissionData: noSubmissionDocuments, }) - const lookupTable = makeDocumentList(submissions) - expect(lookupTable).toEqual({ - currentDocuments: [ + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) + + expect(lookupTable.previousDocuments).toEqual([]) + expect(lookupTable.currentDocuments.sort()).toEqual( + [ '1648242632157-Amerigroup Texas, Inc.pdf', '1648490162641-lifeofgalileo.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - ], - previousDocuments: [], - }) - }) - - it('should return error if any revisions does not decode', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({}) - submissions.revisions[1].node.formDataProto = 'Should return an error' - const lookupTable = makeDocumentList(submissions) - - expect(lookupTable).toEqual( - new Error( - 'Failed to read submission data; unable to display documents' - ) + ].sort() ) }) }) diff --git a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts index 953efd16a9..1cdb36e10e 100644 --- a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts +++ b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts @@ -1,9 +1,10 @@ -import { base64ToDomain } from '../common-code/proto/healthPlanFormDataProto' -import { HealthPlanPackage } from '../gen/gqlClient' import { parseKey } from '../common-code/s3URLEncoding' -import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' +import { RevisionsLookupType } from '../gqlHelpers/fetchHealthPlanPackageWrapper' +import { getAllDocuments } from './getAllDocuments' -export type LookupListType = { +// CurrentPreviousDocsLookup - array of document keys for currentDocuments and previousDocuments + +type DocumentKeyLookupType = { currentDocuments: string[] previousDocuments: string[] } @@ -13,64 +14,51 @@ const getKey = (s3URL: string) => { return key instanceof Error ? null : key } -export function makeDocumentListFromFormDatas( - formDatas: HealthPlanFormDataType[] -): LookupListType { - const docBuckets = [ - 'contractDocuments', - 'rateDocuments', - 'documents', - ] as const - const lookupList: LookupListType = { - currentDocuments: [], - previousDocuments: [], - } +// makeDocumentS3KeyLookup - generates list of currentDocuments and previousDocuments by s3Key +// this is used to to determine if can delete a document from s3 after it removed from the FileUpload UI on the documents pages - ContractDetails, RateDetails, Documents + +function makeDocumentS3KeyLookup( + revisionsLookup: RevisionsLookupType +): DocumentKeyLookupType { + const currentDocumentsSet = new Set() + const previousDocumentsSet = new Set() - for (let index = 0; index < formDatas.length; index++) { - const revisionData = formDatas[index] - docBuckets.forEach((bucket) => { - if (bucket === 'rateDocuments') { - revisionData.rateInfos.forEach((rateInfo) => { - rateInfo[bucket].forEach((doc) => { - const key = getKey(doc.s3URL) - if (key && index === 0) { - lookupList.currentDocuments.push(key) - } else if (key && index > 0) { - lookupList.previousDocuments.push(key) - } - }) + Object.keys(revisionsLookup).forEach( + (revisionId: string, index: number) => { + const revisionData = revisionsLookup[revisionId].formData + const allDocuments = getAllDocuments(revisionData) + if (index === 0) { + allDocuments.forEach((doc) => { + const s3Key = getKey(doc.s3URL) + if (!s3Key) { + console.error( + `makeDocumentS3KeyLookup- Failed to read S3 key for $${doc.name} - ${doc.s3URL}` + ) + // fail silently, just return good documents + } else { + currentDocumentsSet.add(s3Key) + } }) } else { - revisionData[bucket].forEach((doc) => { - const key = getKey(doc.s3URL) - if (key && index === 0) { - lookupList.currentDocuments.push(key) - } else if (key && index > 0) { - lookupList.previousDocuments.push(key) + allDocuments.forEach((doc) => { + const s3Key = getKey(doc.s3URL) + if (!s3Key) { + console.error( + `makeDocumentS3KeyLookup - Failed to read S3 key for $${doc.name} - ${doc.s3URL}` + ) + // fail silently, just return good documents + } else { + previousDocumentsSet.add(s3Key) } }) } - }) - } - - return lookupList -} - -export const makeDocumentList = ( - submissions: HealthPlanPackage -): LookupListType | Error => { - const revisions = submissions.revisions - - const formDatas: HealthPlanFormDataType[] = [] - for (let index = 0; index < revisions.length; index++) { - const revisionData = base64ToDomain(revisions[index].node.formDataProto) - if (revisionData instanceof Error) { - return new Error( - 'Failed to read submission data; unable to display documents' - ) } - formDatas.push(revisionData) + ) + return { + currentDocuments: [...currentDocumentsSet], + previousDocuments: [...previousDocumentsSet], } - - return makeDocumentListFromFormDatas(formDatas) } + +export { makeDocumentS3KeyLookup } +export type {DocumentKeyLookupType} diff --git a/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts b/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts index 008b390424..1c46cc14f1 100644 --- a/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts +++ b/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts @@ -2,6 +2,8 @@ import { useFetchHealthPlanPackageQuery, FetchHealthPlanPackageQuery, useFetchHealthPlanPackageWithQuestionsQuery, + HealthPlanRevision, + HealthPlanPackage, } from '../gen/gqlClient' import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' import { base64ToDomain } from '../common-code/proto/healthPlanFormDataProto' @@ -11,20 +13,24 @@ import { QuerySuccessType, WrappedApolloResultType, } from './apolloQueryWrapper' -import { DocumentDateLookupTable } from '../pages/SubmissionSummary/SubmissionSummary' -import { makeDateTableFromFormData } from '../documentHelpers/makeDocumentDateLookupTable' -import { - LookupListType, - makeDocumentListFromFormDatas, -} from '../documentHelpers/makeDocumentKeyLookupList' import { QueryFunctionOptions } from '@apollo/client' +import { recordJSException } from '../otelHelpers' +import { + DocumentDateLookupTableType, + makeDocumentDateTable, +} from '../documentHelpers/makeDocumentDateLookupTable' + +// ExpandedRevisionsType - HPP revision plus an additional formData field containing values of formDataProto decoded into typescript +type ExpandedRevisionsType = HealthPlanRevision & { + formData: HealthPlanFormDataType +} -// We return a slightly modified version of the wrapped result adding formDatas +type RevisionsLookupType = { [revisionID: string]: ExpandedRevisionsType } +// We return a slightly modified version of the wrapped result adding RevisionsLookup, documentDateLookup, etc // all of these fields will be added to the SUCCESS type type AdditionalParsedDataType = { - formDatas: { [revisionID: string]: HealthPlanFormDataType } - documentDates: DocumentDateLookupTable - documentLists: LookupListType + revisionsLookup: RevisionsLookupType + documentDates: DocumentDateLookupTableType } type ParsedFetchResultType = ApolloResultType< @@ -42,6 +48,33 @@ type WrappedFetchResultWithQuestionsType = WrappedApolloResultType< AdditionalParsedDataType > +// Take health plan package directly from API, decodes all fields and return expanded revisions list. +const buildRevisionsLookup = ( + pkg: HealthPlanPackage +): RevisionsLookupType | Error => { + const expandedRevisions: RevisionsLookupType = {} + for (const revision of pkg.revisions) { + const revisionDecodedFormData = base64ToDomain( + revision.node.formDataProto + ) + + if (revisionDecodedFormData instanceof Error) { + const err = + new Error(`buildRevisionsLookup: proto decoding error. pkg ID: ${pkg.id} revision ID: + ${revision.node.id}. Error message: ${revisionDecodedFormData}`) + recordJSException(err) + return err + } else { + expandedRevisions[revision.node.id] = { + ...revision.node, + formData: revisionDecodedFormData, + } + } + } + + return expandedRevisions +} + function parseProtos( result: QuerySuccessType ): ParsedFetchResultType { @@ -50,12 +83,8 @@ function parseProtos( if (!pkg) { return { ...result, - formDatas: {}, - documentDates: {}, - documentLists: { - currentDocuments: [], - previousDocuments: [], - }, + revisionsLookup: {}, + documentDates: { previousSubmissionDate: null }, } } @@ -70,36 +99,20 @@ function parseProtos( } } - const formDatas: { [revisionID: string]: HealthPlanFormDataType } = {} - for (const revisionEdge of pkg.revisions) { - const revision = revisionEdge.node - const formDataResult = base64ToDomain(revision.formDataProto) - - if (formDataResult instanceof Error) { - const err = - new Error(`useFetchHealthPlanPackageWrapper: proto decoding error. ID: - ${pkg.id}. Error message: ${formDataResult}`) - console.error('Error decoding revision', revision, err) - return { - status: 'ERROR', - error: formDataResult, - } + const revisionsLookup = buildRevisionsLookup(pkg) + if (revisionsLookup instanceof Error) { + return { + status: 'ERROR', + error: revisionsLookup, } + } else { + const documentDates = makeDocumentDateTable(revisionsLookup) - formDatas[revision.id] = formDataResult - } - - const formDatasInOrder = pkg.revisions.map((rEdge) => { - return formDatas[rEdge.node.id] - }) - const documentDates = makeDateTableFromFormData(formDatasInOrder) - const documentLists = makeDocumentListFromFormDatas(formDatasInOrder) - - return { - ...result, - formDatas, - documentDates, - documentLists, + return { + ...result, + revisionsLookup, + documentDates, + } } } @@ -167,4 +180,7 @@ function useFetchHealthPlanPackageWithQuestionsWrapper( export { useFetchHealthPlanPackageWrapper, useFetchHealthPlanPackageWithQuestionsWrapper, + buildRevisionsLookup, } + +export type { ExpandedRevisionsType, RevisionsLookupType } diff --git a/services/app-web/src/hooks/useDocument.tsx b/services/app-web/src/hooks/useDocument.tsx index 8c18242edc..52ef044f72 100644 --- a/services/app-web/src/hooks/useDocument.tsx +++ b/services/app-web/src/hooks/useDocument.tsx @@ -11,24 +11,26 @@ type ParsedDocumentWithLinkType = const useDocument = () => { const { getKey, getURL } = useS3() - - const getDocumentsUrl = useCallback( + const getDocumentsWithS3KeyAndUrl= useCallback( async ,>( documents: Array, bucket: BucketShortName ): Promise> => { + return await Promise.all( documents.map(async (doc) => { const key = getKey(doc.s3URL) if (!key) return { ...doc, + s3Key: null, url: null, } const documentLink = await getURL(key, bucket) return { ...doc, + s3Key: key, url: documentLink, } }) @@ -40,7 +42,8 @@ const useDocument = () => { [getURL, getKey] ) - return { getDocumentsUrl } + + return { getDocumentsWithS3KeyAndUrl } } export { useDocument } diff --git a/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx b/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx index cdae871ad7..261b50ace4 100644 --- a/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx +++ b/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx @@ -45,7 +45,7 @@ export const QATable = ({ round: number user: User }) => { - const { getDocumentsUrl } = useDocument() + const { getDocumentsWithS3KeyAndUrl } = useDocument() const tableDocuments = [ ...question.documents.map((doc) => ({ ...doc, @@ -84,7 +84,7 @@ export const QATable = ({ useDeepCompareEffect(() => { const refreshDocuments = async () => { - const newDocuments = await getDocumentsUrl( + const newDocuments = await getDocumentsWithS3KeyAndUrl( tableDocuments, 'QUESTION_ANSWER_DOCS' ) @@ -94,7 +94,7 @@ export const QATable = ({ } void refreshDocuments() - }, [tableDocuments, getDocumentsUrl, setRefreshedDocs]) + }, [tableDocuments, getDocumentsWithS3KeyAndUrl, setRefreshedDocs]) return ( <> diff --git a/services/app-web/src/pages/StateSubmission/New/NewStateSubmissionForm.test.tsx b/services/app-web/src/pages/StateSubmission/New/NewStateSubmissionForm.test.tsx index f1e717f081..861513ce67 100644 --- a/services/app-web/src/pages/StateSubmission/New/NewStateSubmissionForm.test.tsx +++ b/services/app-web/src/pages/StateSubmission/New/NewStateSubmissionForm.test.tsx @@ -40,6 +40,9 @@ describe('NewStateSubmissionForm', () => { screen.getByRole('form', { name: 'New Submission Form' }) ).toBeInTheDocument() + const medicaid = await screen.findByText('Medicaid') + await userEvent.click(medicaid) + const comboBox = await screen.findByRole('combobox', { name: 'Programs this contract action covers (required)', }) diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx index 2bee86db31..10e1be427c 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx @@ -10,6 +10,7 @@ describe('ReviewSubmit', () => { it('renders without errors', async () => { renderWithProviders( { it('displays edit buttons for every section', async () => { renderWithProviders( { it('does not display zip download buttons', async () => { renderWithProviders( { it('renders info from a DraftSubmission', async () => { renderWithProviders( { it('displays back and save as draft buttons', async () => { renderWithProviders( { it('displays submit button', async () => { renderWithProviders( { diff --git a/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx b/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx index 478f1c4330..5eb8b859da 100644 --- a/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx +++ b/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx @@ -44,6 +44,10 @@ import { recordJSException } from '../../otelHelpers/tracingHelper' import { useStatePrograms } from '../../hooks/useStatePrograms' import { ApolloError } from '@apollo/client' import { handleApolloError } from '../../gqlHelpers/apolloErrors' +import { + makeDocumentS3KeyLookup, + makeDocumentDateTable, +} from '../../documentHelpers' const getRelativePathFromNestedRoute = (formRouteType: RouteT): string => getRelativePath({ @@ -98,9 +102,9 @@ const activeFormPages = ( ) } -/* +/* Prep work for refactor of form pages. This should be pulled out into a HealthPlanFormPageContext or HOC. - We have several instances of shared state across pages. + We have several instances of shared state across pages. */ export type HealthPlanFormPageProps = { @@ -196,7 +200,7 @@ export const StateSubmissionForm = (): React.ReactElement => { return // api failure or protobuf decode failure } - const { data, formDatas, documentDates, documentLists } = fetchResult + const { data, revisionsLookup } = fetchResult const pkg = data.fetchHealthPlanPackage.pkg // fetchHPP returns null if no package is found with the given ID @@ -204,9 +208,12 @@ export const StateSubmissionForm = (): React.ReactElement => { return } - // pull out the latest revision for editing + // pull out the latest revision and document lookups const latestRevision = pkg.revisions[0].node - const formDataFromLatestRevision = formDatas[latestRevision.id] + const formDataFromLatestRevision = + revisionsLookup[latestRevision.id].formData + const documentDates = makeDocumentDateTable(revisionsLookup) + const documentLists = makeDocumentS3KeyLookup(revisionsLookup) // if we've gotten back a submitted revision, it can't be edited if (formDataFromLatestRevision.status !== 'DRAFT') { @@ -311,9 +318,9 @@ export const StateSubmissionForm = (): React.ReactElement => { element={ } /> diff --git a/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.test.tsx b/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.test.tsx index 3d243e7a34..f7d0d16a81 100644 --- a/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.test.tsx +++ b/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.test.tsx @@ -3,10 +3,7 @@ import userEvent from '@testing-library/user-event' import { screen, waitFor, within } from '@testing-library/react' import selectEvent from 'react-select-event' import { fetchCurrentUserMock } from '../../../testHelpers/apolloMocks' -import { - ldUseClientSpy, - renderWithProviders, -} from '../../../testHelpers/jestHelpers' +import { renderWithProviders } from '../../../testHelpers/jestHelpers' import { SubmissionType, SubmissionTypeFormValues } from './' import { Formik } from 'formik' import { contractOnly } from '../../../common-code/healthPlanFormDataMocks' @@ -50,20 +47,20 @@ describe('SubmissionType', () => { ) expect( - await screen.getByRole('form', { name: 'Submission Type Form' }) + screen.getByRole('form', { name: 'Submission Type Form' }) ).toBeInTheDocument() expect( - await screen.getByRole('button', { + screen.getByRole('button', { name: 'Save as draft', }) ).toBeDefined() expect( - await screen.getByRole('button', { + screen.getByRole('button', { name: 'Cancel', }) ).toBeDefined() expect( - await screen.getByRole('button', { + screen.getByRole('button', { name: 'Continue', }) ).toBeDefined() @@ -80,306 +77,299 @@ describe('SubmissionType', () => { }) expect( - await screen.getByRole('form', { name: 'New Submission Form' }) + screen.getByRole('form', { name: 'New Submission Form' }) ).toBeInTheDocument() expect( - await screen.queryByRole('button', { + screen.queryByRole('button', { name: 'Save as draft', }) ).toBeNull() expect( - await screen.getByRole('button', { + screen.getByRole('button', { name: 'Cancel', }) ).toBeDefined() expect( - await screen.getByRole('button', { + screen.getByRole('button', { name: 'Continue', }) ).toBeDefined() }) - describe('Feature flagged population coverage questions', () => { - beforeEach(() => { - ldUseClientSpy({ 'chip-only-form': true }) - }) - it('displays population coverage question', async () => { - renderWithProviders( - - - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } + it('displays population coverage question', async () => { + renderWithProviders( + + + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + } + ) + + expect( + screen.getByText( + 'Which populations does this contract action cover?' ) + ).toBeInTheDocument() + expect( + screen.getByRole('radio', { name: 'Medicaid' }) + ).toBeInTheDocument() + expect( + screen.getByRole('radio', { name: 'CHIP-only' }) + ).toBeInTheDocument() + expect( + screen.getByRole('radio', { name: 'Medicaid and CHIP' }) + ).toBeInTheDocument() + }) + it('disables contract and rates submission type radio and displays hint when CHIP only is selected', async () => { + renderWithProviders( + + + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + } + ) - expect( - await screen.getByText( - 'Which populations does this contract action cover?' - ) - ).toBeInTheDocument() - expect( - await screen.getByRole('radio', { name: 'Medicaid' }) - ).toBeInTheDocument() - expect( - await screen.getByRole('radio', { name: 'CHIP-only' }) - ).toBeInTheDocument() - expect( - await screen.getByRole('radio', { name: 'Medicaid and CHIP' }) - ).toBeInTheDocument() - }) - it('disables contract and rates submission type radio and displays hint when CHIP only is selected', async () => { - renderWithProviders( - - - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } + expect( + screen.getByText( + 'Which populations does this contract action cover?' ) + ).toBeInTheDocument() + const chipOnlyRadio = screen.getByRole('radio', { + name: 'CHIP-only', + }) - expect( - await screen.getByText( - 'Which populations does this contract action cover?' - ) - ).toBeInTheDocument() - const chipOnlyRadio = await screen.getByRole('radio', { - name: 'CHIP-only', - }) + // Select Chip only population coverage + await userEvent.click(chipOnlyRadio) - // Select Chip only population coverage - await userEvent.click(chipOnlyRadio) + // Contract and rates radio is disabled + expect( + screen.getByRole('radio', { + name: 'Contract action and rate certification', + }) + ).toHaveAttribute('disabled') - // Contract and rates radio is disabled - expect( - await screen.getByRole('radio', { - name: 'Contract action and rate certification', - }) - ).toHaveAttribute('disabled') + // Shows hint for submission type + expect( + screen.getByText( + 'States are not required to submit rates with CHIP-only contracts.' + ) + ).toBeInTheDocument() + }) + it('switches submission type to contract only when changing existing population coverage to CHIP-only', async () => { + renderWithProviders( + + + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + } + ) - // Shows hint for submission type - expect( - await screen.getByText( - 'States are not required to submit rates with CHIP-only contracts.' - ) - ).toBeInTheDocument() - }) - it('switches submission type to contract only when changing existing population coverage to CHIP-only', async () => { - renderWithProviders( - - - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } + expect( + screen.getByText( + 'Which populations does this contract action cover?' ) + ).toBeInTheDocument() + const medicaidRadio = screen.getByRole('radio', { + name: 'Medicaid', + }) + const contractAndRatesRadio = screen.getByRole('radio', { + name: 'Contract action and rate certification', + }) - expect( - await screen.getByText( - 'Which populations does this contract action cover?' - ) - ).toBeInTheDocument() - const medicaidRadio = await screen.getByRole('radio', { - name: 'Medicaid', - }) - const contractAndRatesRadio = await screen.getByRole('radio', { - name: 'Contract action and rate certification', - }) + // Click Medicaid population coverage radio + await userEvent.click(medicaidRadio) - // Click Medicaid population coverage radio - await userEvent.click(medicaidRadio) + // Click contract and rates submission type + await userEvent.click(contractAndRatesRadio) - // Click contract and rates submission type - await userEvent.click(contractAndRatesRadio) + // Expect contract and rates radio to be selected + expect(contractAndRatesRadio).toBeChecked() - // Expect contract and rates radio to be selected - expect(contractAndRatesRadio).toBeChecked() + const chipOnlyRadio = screen.getByRole('radio', { + name: 'CHIP-only', + }) + const contractOnlyRadio = screen.getByRole('radio', { + name: 'Contract action only', + }) - const chipOnlyRadio = await screen.getByRole('radio', { - name: 'CHIP-only', - }) - const contractOnlyRadio = await screen.getByRole('radio', { - name: 'Contract action only', - }) + // Change population coverage to Chip only + await userEvent.click(chipOnlyRadio) - // Change population coverage to Chip only - await userEvent.click(chipOnlyRadio) + // Contract and rates radio is unselected and disabled + expect(contractAndRatesRadio).not.toBeChecked() + expect(contractAndRatesRadio).toHaveAttribute('disabled') - // Contract and rates radio is unselected and disabled - expect(contractAndRatesRadio).not.toBeChecked() - expect(contractAndRatesRadio).toHaveAttribute('disabled') + // Contract only radio is selected + expect(contractOnlyRadio).toBeChecked() - // Contract only radio is selected - expect(contractOnlyRadio).toBeChecked() + // Shows hint for submission type + expect( + screen.getByText( + 'States are not required to submit rates with CHIP-only contracts.' + ) + ).toBeInTheDocument() + }) + it('new submissions does not automatically select contract only submission type when selecting CHIP-only coverage', async () => { + renderWithProviders( + + + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + } + ) - // Shows hint for submission type - expect( - await screen.getByText( - 'States are not required to submit rates with CHIP-only contracts.' - ) - ).toBeInTheDocument() - }) - it('new submissions does not automatically select contract only submission type when selecting CHIP-only coverage', async () => { - renderWithProviders( - - - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } + expect( + screen.getByText( + 'Which populations does this contract action cover?' ) + ).toBeInTheDocument() + const medicaidRadio = screen.getByRole('radio', { + name: 'Medicaid', + }) + const chipOnlyRadio = screen.getByRole('radio', { + name: 'CHIP-only', + }) + const medicaidAndChipRadio = screen.getByRole('radio', { + name: 'Medicaid and CHIP', + }) + const contractAndRatesRadio = screen.getByRole('radio', { + name: 'Contract action and rate certification', + }) + const contractOnlyRadio = screen.getByRole('radio', { + name: 'Contract action only', + }) - expect( - await screen.getByText( - 'Which populations does this contract action cover?' - ) - ).toBeInTheDocument() - const medicaidRadio = await screen.getByRole('radio', { - name: 'Medicaid', - }) - const chipOnlyRadio = await screen.getByRole('radio', { - name: 'CHIP-only', - }) - const medicaidAndChipRadio = await screen.getByRole('radio', { - name: 'Medicaid and CHIP', - }) - const contractAndRatesRadio = await screen.getByRole('radio', { - name: 'Contract action and rate certification', - }) - const contractOnlyRadio = await screen.getByRole('radio', { - name: 'Contract action only', - }) + // Click on each of the population covered radios + await userEvent.click(medicaidRadio) + await userEvent.click(chipOnlyRadio) + await userEvent.click(medicaidAndChipRadio) - // Click on each of the population covered radios - await userEvent.click(medicaidRadio) - await userEvent.click(chipOnlyRadio) - await userEvent.click(medicaidAndChipRadio) + // Expect contract and rates radio to not be selected + expect(contractAndRatesRadio).not.toBeChecked() + expect(contractOnlyRadio).not.toBeChecked() - // Expect contract and rates radio to not be selected - expect(contractAndRatesRadio).not.toBeChecked() - expect(contractOnlyRadio).not.toBeChecked() + // Change population coverage to Chip only + await userEvent.click(chipOnlyRadio) - // Change population coverage to Chip only - await userEvent.click(chipOnlyRadio) + // Expect the submission type radios to still be unselected + expect(contractAndRatesRadio).not.toBeChecked() + expect(contractOnlyRadio).not.toBeChecked() - // Expect the submission type radios to still be unselected - expect(contractAndRatesRadio).not.toBeChecked() - expect(contractOnlyRadio).not.toBeChecked() + // Shows hint for submission type + expect( + screen.getByText( + 'States are not required to submit rates with CHIP-only contracts.' + ) + ).toBeInTheDocument() + }) + it('does not clear contract only submission type radio when switching to CHIP-only population coverage', async () => { + renderWithProviders( + + + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + } + ) - // Shows hint for submission type - expect( - await screen.getByText( - 'States are not required to submit rates with CHIP-only contracts.' - ) - ).toBeInTheDocument() - }) - it('does not clear contract only submission type radio when switching to CHIP-only population coverage', async () => { - renderWithProviders( - - - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } + expect( + screen.getByText( + 'Which populations does this contract action cover?' ) + ).toBeInTheDocument() + const medicaidRadio = screen.getByRole('radio', { + name: 'Medicaid', + }) + const contractOnlyRadio = screen.getByRole('radio', { + name: 'Contract action only', + }) - expect( - await screen.getByText( - 'Which populations does this contract action cover?' - ) - ).toBeInTheDocument() - const medicaidRadio = await screen.getByRole('radio', { - name: 'Medicaid', - }) - const contractOnlyRadio = await screen.getByRole('radio', { - name: 'Contract action only', - }) + // Click Medicaid population coverage radio + await userEvent.click(medicaidRadio) - // Click Medicaid population coverage radio - await userEvent.click(medicaidRadio) + // Click contract only submission type + await userEvent.click(contractOnlyRadio) - // Click contract only submission type - await userEvent.click(contractOnlyRadio) + // Expect contract only radio to be selected + expect(contractOnlyRadio).toBeChecked() - // Expect contract only radio to be selected - expect(contractOnlyRadio).toBeChecked() + const chipOnlyRadio = screen.getByRole('radio', { + name: 'CHIP-only', + }) - const chipOnlyRadio = await screen.getByRole('radio', { - name: 'CHIP-only', - }) + // Change population coverage to Chip only + await userEvent.click(chipOnlyRadio) - // Change population coverage to Chip only - await userEvent.click(chipOnlyRadio) + // Contract only radio is still selected + expect(contractOnlyRadio).toBeChecked() + }) + it('shows validation message when population coverage is not selected', async () => { + renderWithProviders( + + + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + } + ) - // Contract only radio is still selected - expect(contractOnlyRadio).toBeChecked() - }) - it('shows validation message when population coverage is not selected', async () => { - renderWithProviders( - - - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } + // Expect population coverage question and radios + expect( + screen.getByText( + 'Which populations does this contract action cover?' ) + ).toBeInTheDocument() + expect( + screen.getByRole('radio', { name: 'Medicaid' }) + ).toBeInTheDocument() + expect( + screen.getByRole('radio', { name: 'CHIP-only' }) + ).toBeInTheDocument() + expect( + screen.getByRole('radio', { name: 'Medicaid and CHIP' }) + ).toBeInTheDocument() - // Expect population coverage question and radios - expect( - await screen.getByText( - 'Which populations does this contract action cover?' - ) - ).toBeInTheDocument() - expect( - await screen.getByRole('radio', { name: 'Medicaid' }) - ).toBeInTheDocument() - expect( - await screen.getByRole('radio', { name: 'CHIP-only' }) - ).toBeInTheDocument() - expect( - await screen.getByRole('radio', { name: 'Medicaid and CHIP' }) - ).toBeInTheDocument() - - // Test validations work. - await userEvent.click( - screen.getByRole('button', { name: 'Continue' }) - ) - await screen.findByTestId('error-summary') - await screen.findAllByText( - 'You must select the population this contract covers' - ) - }) + // Test validations work. + await userEvent.click(screen.getByRole('button', { name: 'Continue' })) + await screen.findByTestId('error-summary') + await screen.findAllByText( + 'You must select the population this contract covers' + ) }) it('displays programs select dropdown', async () => { @@ -398,7 +388,7 @@ describe('SubmissionType', () => { ) expect( - await screen.getByRole('combobox', { + screen.getByRole('combobox', { name: 'Programs this contract action covers (required)', }) ).toBeInTheDocument() @@ -444,14 +434,14 @@ describe('SubmissionType', () => { ) const combobox = await screen.findByRole('combobox') - await selectEvent.openMenu(combobox) + selectEvent.openMenu(combobox) await waitFor(() => { expect(screen.getByText('Program 3')).toBeInTheDocument() }) await selectEvent.select(combobox, 'Program 1') - await selectEvent.openMenu(combobox) + selectEvent.openMenu(combobox) await selectEvent.select(combobox, 'Program 3') // in react-select, only items that are selected have a "remove item" label @@ -475,10 +465,10 @@ describe('SubmissionType', () => { ) expect( - await screen.getByRole('radio', { name: 'Contract action only' }) + screen.getByRole('radio', { name: 'Contract action only' }) ).toBeInTheDocument() expect( - await screen.getByRole('radio', { + screen.getByRole('radio', { name: 'Contract action and rate certification', }) ).toBeInTheDocument() @@ -500,10 +490,10 @@ describe('SubmissionType', () => { ) expect( - await screen.getByRole('radio', { name: 'Base contract' }) + screen.getByRole('radio', { name: 'Base contract' }) ).toBeInTheDocument() expect( - await screen.getByRole('radio', { + screen.getByRole('radio', { name: 'Amendment to base contract', }) ).toBeInTheDocument() @@ -565,7 +555,7 @@ describe('SubmissionType', () => { } ) expect( - await screen.getByRole('textbox', { + screen.getByRole('textbox', { name: 'Submission description', }) ).toBeInTheDocument() @@ -582,14 +572,14 @@ describe('SubmissionType', () => { } ) - expect(await screen.getByRole('textbox')).not.toHaveClass( + expect(screen.getByRole('textbox')).not.toHaveClass( 'usa-input--error' ) expect( - await screen.queryByText('You must choose a submission type') + screen.queryByText('You must choose a submission type') ).toBeNull() expect( - await screen.queryByText( + screen.queryByText( 'You must provide a description of any major changes or updates' ) ).toBeNull() @@ -612,7 +602,7 @@ describe('SubmissionType', () => { name: 'Submission description', }) - expect(await textarea).toBeInTheDocument() + expect(textarea).toBeInTheDocument() //trigger validation await userEvent.type(textarea, 'something') @@ -643,7 +633,7 @@ describe('SubmissionType', () => { name: 'Submission description', }) - expect(await textarea).toBeInTheDocument() + expect(textarea).toBeInTheDocument() const submissionType = await screen.findByText( 'Contract action only' diff --git a/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.tsx b/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.tsx index 6dd00cd686..05d5d600e8 100644 --- a/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.tsx +++ b/services/app-web/src/pages/StateSubmission/SubmissionType/SubmissionType.tsx @@ -44,8 +44,6 @@ import { yesNoFormValueAsBoolean, } from '../../../components/Form/FieldYesNo/FieldYesNo' import { SubmissionTypeFormSchema } from './SubmissionTypeSchema' -import { useLDClient } from 'launchdarkly-react-client-sdk' -import { featureFlags } from '../../../common-code/featureFlags' export interface SubmissionTypeFormValues { populationCovered?: PopulationCoveredType @@ -79,12 +77,6 @@ export const SubmissionType = ({ const location = useLocation() const isNewSubmission = location.pathname === '/submissions/new' - const ldClient = useLDClient() - const showCHIPOnlyForm = ldClient?.variation( - featureFlags.CHIP_ONLY_FORM.flag, - featureFlags.CHIP_ONLY_FORM.defaultValue - ) - const [createHealthPlanPackage, { error }] = useCreateHealthPlanPackageMutation({ // This function updates the Apollo Client Cache after we create a new DraftSubmission @@ -162,6 +154,13 @@ export const SubmissionType = ({ ) => { if (isNewSubmission) { try { + if (!values.populationCovered) { + console.info( + 'unexpected error, attempting to submit without population covered', + values.submissionType + ) + return + } if ( !( values.submissionType === 'CONTRACT_ONLY' || @@ -289,9 +288,7 @@ export const SubmissionType = ({ {({ values, @@ -326,78 +323,71 @@ export const SubmissionType = ({ headingRef={errorSummaryHeadingRef} /> )} - - {showCHIPOnlyForm && ( - +
+ {showFieldErrors( errors.populationCovered + ) && ( + + {errors.populationCovered} + )} - > -
- {showFieldErrors( - errors.populationCovered - ) && ( - - {errors.populationCovered} - - )} - - handlePopulationCoveredClick( - 'MEDICAID', - values, - setFieldValue - ) - } - /> - - handlePopulationCoveredClick( - 'MEDICAID_AND_CHIP', - values, - setFieldValue - ) - } - /> - - handlePopulationCoveredClick( - 'CHIP', - values, - setFieldValue - ) - } - /> -
- - )} + + handlePopulationCoveredClick( + 'MEDICAID', + values, + setFieldValue + ) + } + /> + + handlePopulationCoveredClick( + 'MEDICAID_AND_CHIP', + values, + setFieldValue + ) + } + /> + + handlePopulationCoveredClick( + 'CHIP', + values, + setFieldValue + ) + } + /> +
+
- {showCHIPOnlyForm && - values.populationCovered === 'CHIP' && ( -
- States are not required to - submit rates with CHIP-only - contracts. -
- )} + {values.populationCovered === 'CHIP' && ( +
+ States are not required to submit + rates with CHIP-only contracts. +
+ )}
+const SubmissionTypeFormSchema = (_flags: FeatureFlagSettings = {}) => Yup.object().shape({ - populationCovered: flags['chip-only-form'] - ? Yup.string().required( - 'You must select the population this contract covers' - ) - : Yup.string().optional(), + populationCovered: Yup.string().required( + 'You must select the population this contract covers' + ), programIDs: Yup.array().min(1, 'You must select at least one program'), submissionType: Yup.string().required( 'You must choose a submission type' diff --git a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx index 056fa36ce7..cf1cb7607c 100644 --- a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx @@ -84,7 +84,12 @@ describe('SubmissionRevisionSummary', () => { expect(rows).toHaveLength(2) expect(within(rows[0]).getByText('Date added')).toBeInTheDocument() expect( - within(rows[1]).getByText(dayjs(new Date()).format('M/D/YY')) + within(rows[1]).getByText( + dayjs( + mockSubmittedHealthPlanPackageWithRevisions() + .revisions[2]?.node?.submitInfo?.updatedAt + ).format('M/D/YY') + ) ).toBeInTheDocument() }) }) diff --git a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx index 3c59024a96..691e7a1645 100644 --- a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx +++ b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx @@ -60,7 +60,7 @@ export const SubmissionRevisionSummary = (): React.ReactElement => { return // api failure or protobuf decode failure } - const { data, formDatas, documentDates } = fetchResult + const { data, revisionsLookup, documentDates } = fetchResult const pkg = data.fetchHealthPlanPackage.pkg // fetchHPP returns null if no package is found with the given ID @@ -77,7 +77,7 @@ export const SubmissionRevisionSummary = (): React.ReactElement => { console.info('no revision found at index', revisionIndex) return } - const packageData = formDatas[revision.id] + const packageData = revisionsLookup[revision.id].formData const statePrograms = pkg.state.programs const name = packageName(packageData, statePrograms) diff --git a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx index 0cad8319bb..cbcd0eae30 100644 --- a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx +++ b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx @@ -28,14 +28,17 @@ import { } from '../../common-code/healthPlanFormDataType' import { useLDClient } from 'launchdarkly-react-client-sdk' import { featureFlags } from '../../common-code/featureFlags' -import { DocumentDateLookupTable } from '../SubmissionSummary/SubmissionSummary' +import { + DocumentDateLookupTableType, + makeDocumentDateTable, +} from '../../documentHelpers/makeDocumentDateLookupTable' export type SideNavOutletContextType = { pkg: HealthPlanPackage packageName: string currentRevision: HealthPlanRevision packageData: HealthPlanFormDataType - documentDates: DocumentDateLookupTable + documentDates: DocumentDateLookupTableType user: User } @@ -86,7 +89,7 @@ export const SubmissionSideNav = () => { ) } - const { data, formDatas, documentDates } = fetchResult + const { data, revisionsLookup } = fetchResult const pkg = data.fetchHealthPlanPackage.pkg // Display generic error page if getting logged in user returns undefined. @@ -98,7 +101,6 @@ export const SubmissionSideNav = () => { if (!pkg) { return } - const submissionStatus = pkg.status const isCMSUser = loggedInUser?.role === 'CMS_USER' @@ -130,9 +132,9 @@ export const SubmissionSideNav = () => { return } const currentRevision = edge.node - const packageData = formDatas[currentRevision.id] + const packageData = revisionsLookup[currentRevision.id].formData const pkgName = packageName(packageData, pkg.state.programs) - + const documentDates = makeDocumentDateTable(revisionsLookup) const outletContext: SideNavOutletContextType = { pkg, packageName: pkgName, diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx index 8b4cdfa5fb..a12b16471e 100644 --- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx @@ -367,13 +367,13 @@ describe('SubmissionSummary', () => { it('extracts the correct dates from the submission and displays them in tables', async () => { const submission = mockSubmittedHealthPlanPackageWithRevision({ - currentSubmissionData: { + currentSubmitInfo: { updatedAt: new Date('2022-05-12T21:13:20.420Z'), }, - previousSubmissionData: { + previousSubmitInfo: { updatedAt: new Date('2022-04-12T21:13:20.420Z'), }, - initialSubmissionData: { + initialSubmitInfo: { updatedAt: new Date('2022-03-12T21:13:20.420Z'), }, }) @@ -408,7 +408,7 @@ describe('SubmissionSummary', () => { ) await waitFor(() => { const rows = screen.getAllByRole('row') - expect(rows).toHaveLength(10) + expect(rows).toHaveLength(8) expect( within(rows[0]).getByText('Date added') ).toBeInTheDocument() @@ -523,7 +523,7 @@ describe('SubmissionSummary', () => { 'src/common-code/proto/healthPlanFormDataProto/testData/' ) .filter((f) => f.endsWith('.proto')) - /* as much as we'd like to loop over all the proto files here, looping and async tests, + /* as much as we'd like to loop over all the proto files here, looping and async tests, which this one is (document loading) produces inconsistent results. We have to copy/paste the test for each proto file. */ it('loads outdated health plan packages with old protos as expected - 0', async () => { @@ -582,10 +582,10 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('8/19/22') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract-supporting') expect(rows[3]).toHaveTextContent('rates cert 1') - expect(rows[3]).toHaveTextContent('8/19/22') + expect(rows[3]).toHaveTextContent('11/2/21') expect(rows[3]).toHaveTextContent('Rate certification') expect(rows[4]).toHaveTextContent('rates cert 2') expect(document.body).toHaveTextContent(/Submission type/) @@ -714,9 +714,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -742,9 +740,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { @@ -955,10 +951,10 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('5/13/21') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract') expect(rows[3]).toHaveTextContent('contract doc') - expect(rows[3]).toHaveTextContent('5/13/21') + expect(rows[3]).toHaveTextContent('1/2/21') expect(rows[3]).toHaveTextContent('Contract-supporting') expect(rows[4]).toHaveTextContent('Document') expect(document.body).toHaveTextContent(/Submission type/) @@ -1086,9 +1082,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -1114,9 +1108,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { @@ -1322,17 +1314,17 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('5/13/21') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract') expect(rows[2]).toHaveTextContent('Document') expect(rows[3]).toHaveTextContent('contract doc') - expect(rows[3]).toHaveTextContent('5/13/21') + expect(rows[3]).toHaveTextContent('1/2/21') expect(rows[3]).toHaveTextContent('Contract-supporting') expect(rows[4]).toHaveTextContent('Document') expect(rows[5]).toHaveTextContent('rates cert 1') - expect(rows[5]).toHaveTextContent('5/13/21') + expect(rows[5]).toHaveTextContent('1/2/21') expect(rows[5]).toHaveTextContent('Rate certification') - expect(rows[6]).toHaveTextContent('5/13/21') + expect(rows[6]).toHaveTextContent('1/2/21') expect(document.body).toHaveTextContent(/Submission type/) expect( screen.getByRole('heading', { @@ -1458,9 +1450,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -1486,9 +1476,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { @@ -1693,17 +1681,17 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('5/13/21') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract') expect(rows[2]).toHaveTextContent('Document') expect(rows[3]).toHaveTextContent('contract doc') - expect(rows[3]).toHaveTextContent('5/13/21') + expect(rows[3]).toHaveTextContent('1/2/21') expect(rows[3]).toHaveTextContent('Contract-supporting') expect(rows[4]).toHaveTextContent('Document') expect(rows[5]).toHaveTextContent('rates cert 1') - expect(rows[5]).toHaveTextContent('5/13/21') + expect(rows[5]).toHaveTextContent('1/2/21') expect(rows[5]).toHaveTextContent('Rate certification') - expect(rows[6]).toHaveTextContent('5/13/21') + expect(rows[6]).toHaveTextContent('1/2/21') expect(document.body).toHaveTextContent(/Submission type/) expect( screen.getByRole('heading', { @@ -1829,9 +1817,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -1857,9 +1843,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx index b84ad2ad4f..5069614102 100644 --- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx +++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx @@ -28,10 +28,6 @@ import { useLDClient } from 'launchdarkly-react-client-sdk' import { featureFlags } from '../../common-code/featureFlags' import { SideNavOutletContextType } from '../SubmissionSideNav/SubmissionSideNav' -export type DocumentDateLookupTable = { - [key: string]: string -} - function UnlockModalButton({ disabled, modalRef, @@ -164,17 +160,17 @@ export const SubmissionSummary = (): React.ReactElement => { initiallySubmittedAt={pkg.initiallySubmittedAt} /> {isContractActionAndRateCertification && ( diff --git a/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts b/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts index 77910acc2d..60f2ec88b8 100644 --- a/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts +++ b/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts @@ -18,6 +18,7 @@ import { UpdateHealthPlanFormDataMutation, CreateHealthPlanPackageDocument, CreateHealthPlanPackageMutation, + HealthPlanRevision, } from '../../gen/gqlClient' import { mockContractAndRatesDraft, @@ -125,12 +126,18 @@ const fetchStateHealthPlanPackageMockSuccess = ({ const mockSubmittedHealthPlanPackageWithRevision = ({ currentSubmissionData, + currentSubmitInfo, previousSubmissionData, + previousSubmitInfo, initialSubmissionData, + initialSubmitInfo, }: { currentSubmissionData?: Partial + currentSubmitInfo?: Partial previousSubmissionData?: Partial + previousSubmitInfo?: Partial initialSubmissionData?: Partial + initialSubmitInfo?: Partial }): HealthPlanPackage => { const currentFiles: Partial = { contractDocuments: [ @@ -151,15 +158,21 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ { s3URL: 's3://bucketname/1648242665634-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf', name: 'Amerigroup Texas, Inc.pdf', - documentCategories: ['RATES_RELATED'], + documentCategories: ['RATES'], }, { s3URL: 's3://bucketname/1648242711421-Amerigroup Texas Inc copy.pdf/Amerigroup Texas Inc copy.pdf', name: 'Amerigroup Texas Inc copy.pdf', + documentCategories: ['RATES'], + }, + ], + supportingDocuments: [ + { + s3URL: 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', + name: 'covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', documentCategories: ['RATES_RELATED'], }, ], - supportingDocuments: [], actuaryContacts: [], packagesWithSharedRateCerts: [], }, @@ -170,11 +183,6 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ name: '529-10-0020-00003_Superior_Health Plan, Inc.pdf', documentCategories: ['CONTRACT_RELATED'], }, - { - s3URL: 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - name: 'covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - documentCategories: ['RATES_RELATED'], - }, ], } const previousFiles: Partial = { @@ -214,7 +222,13 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ documentCategories: ['RATES'], }, ], - supportingDocuments: [], + supportingDocuments: [ + { + s3URL: 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf', + name: '529-10-0020-00003_Superior_Health Plan, Inc.pdf', + documentCategories: ['RATES_RELATED'], + }, + ], actuaryContacts: [], packagesWithSharedRateCerts: [], }, @@ -230,11 +244,6 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ name: 'Amerigroup Texas Inc copy.pdf', documentCategories: ['CONTRACT_RELATED'], }, - { - s3URL: 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf', - name: '529-10-0020-00003_Superior_Health Plan, Inc.pdf', - documentCategories: ['RATES_RELATED'], - }, ], } @@ -243,16 +252,36 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ ...currentFiles, ...currentSubmissionData, }) + const currentSubmit = { + updatedAt: '2022-03-28T17:56:32.952Z', + updatedBy: 'aang@example.com', + updatedReason: 'Placeholder resubmission reason', + ...currentSubmitInfo, + } const previousProto = domainToBase64({ ...unlockedWithALittleBitOfEverything(), ...previousFiles, ...previousSubmissionData, }) + + const previousSubmit = { + updatedAt: '2022-03-25T21:14:43.057Z', + updatedBy: 'aang@example.com', + updatedReason: 'Placeholder resubmission reason', + ...previousSubmitInfo, + } const initialProto = domainToBase64({ ...mockContractAndRatesDraft(), ...previousFiles, ...initialSubmissionData, }) + + const initialSubmit = { + updatedAt: '2022-03-25T21:13:20.419Z', + updatedBy: 'aang@example.com', + updatedReason: 'Initial submission', + ...initialSubmitInfo, + } return { __typename: 'HealthPlanPackage', id: '07f9efbf-d4d1-44ae-8674-56d9d6b75ce6', @@ -278,9 +307,7 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ }, submitInfo: { __typename: 'UpdateInformation', - updatedAt: '2022-03-28T17:56:32.952Z', - updatedBy: 'aang@example.com', - updatedReason: 'Placeholder resubmission reason', + ...currentSubmit, }, createdAt: '2022-03-28T17:54:39.175Z', formDataProto: currentProto, @@ -299,9 +326,7 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ }, submitInfo: { __typename: 'UpdateInformation', - updatedAt: '2022-03-25T21:14:43.057Z', - updatedBy: 'aang@example.com', - updatedReason: 'Placeholder resubmission reason', + ...previousSubmit, }, createdAt: '2022-03-25T21:13:56.176Z', formDataProto: previousProto, @@ -315,9 +340,7 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ unlockInfo: null, submitInfo: { __typename: 'UpdateInformation', - updatedAt: '2022-03-25T21:13:20.419Z', - updatedBy: 'aang@example.com', - updatedReason: 'Initial submission', + ...initialSubmit, }, createdAt: '2022-03-25T03:28:56.244Z', formDataProto: initialProto, diff --git a/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts b/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts index 5c16fced3d..5f8bc3e0b1 100644 --- a/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts +++ b/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts @@ -8,7 +8,6 @@ describe('Q&A', () => { it('can add questions and responses', () => { cy.interceptFeatureFlags({ 'cms-questions': true, - 'chip-only-form': true, }) // Assign Division to CMS user zuko diff --git a/services/cypress/support/launchDarklyCommands.ts b/services/cypress/support/launchDarklyCommands.ts index f72cf7bd2c..74767a8f32 100644 --- a/services/cypress/support/launchDarklyCommands.ts +++ b/services/cypress/support/launchDarklyCommands.ts @@ -84,7 +84,6 @@ Cypress.Commands.add('stubFeatureFlags', () => { * Useful if you want default feature flags for tests that are different than default values set in common-code featureFlags **/ cy.interceptFeatureFlags({ - 'chip-only-form': true, 'packages-with-shared-rates': true, }) }) diff --git a/services/cypress/support/stateSubmissionFormCommands.ts b/services/cypress/support/stateSubmissionFormCommands.ts index 3243d61d2e..a6edcaab09 100644 --- a/services/cypress/support/stateSubmissionFormCommands.ts +++ b/services/cypress/support/stateSubmissionFormCommands.ts @@ -1,4 +1,3 @@ -import { aliasMutation } from '../utils/graphql-test-utils' Cypress.Commands.add('startNewContractOnlySubmissionWithBaseContract', () => { // Must be on '/submissions/new' cy.findByTestId('dashboard-page').should('exist') @@ -37,11 +36,7 @@ Cypress.Commands.add('startNewContractAndRatesSubmission', () => { Cypress.Commands.add('fillOutContractActionOnlyWithBaseContract', () => { // Must be on '/submissions/new' - cy.getFeatureFlagStore(['chip-only-form']).then((store) => { - if (store['chip-only-form']) { - cy.get('label[for="medicaid"]').click() - } - }) + cy.get('label[for="medicaid"]').click() cy.findByRole('combobox', { name: 'Programs this contract action covers (required)', @@ -63,11 +58,7 @@ Cypress.Commands.add('fillOutContractActionOnlyWithBaseContract', () => { Cypress.Commands.add('fillOutContractActionOnlyWithAmendment', () => { // Must be on '/submissions/new' - cy.getFeatureFlagStore(['chip-only-form']).then((store) => { - if (store['chip-only-form']) { - cy.get('label[for="medicaid"]').click() - } - }) + cy.get('label[for="medicaid"]').click() cy.findByRole('combobox', { name: 'Programs this contract action covers (required)', timeout: 2_000 }).click({ @@ -89,11 +80,7 @@ Cypress.Commands.add('fillOutContractActionOnlyWithAmendment', () => { Cypress.Commands.add('fillOutContractActionAndRateCertification', () => { // Must be on '/submissions/new' - cy.getFeatureFlagStore(['chip-only-form']).then((store) => { - if (store['chip-only-form']) { - cy.get('label[for="medicaid"]').click() - } - }) + cy.get('label[for="medicaid"]').click() cy.findByRole('combobox', { name: 'Programs this contract action covers (required)', timeout: 2_000 }).click({ diff --git a/yarn.lock b/yarn.lock index 79a27726b2..e307ce4227 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,17 +48,17 @@ leven "^3.1.0" "@apollo/client@^3.4.15": - version "3.7.10" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.10.tgz#addc5fcebaf016981d9476268a06d529be83f568" - integrity sha512-/k1MfrqPKYiPNdHcOzdxg9cEx96vhAGxAcSorzfBvV29XtFQcYW2cPNQOTjK/fpSMtqVo8UNmu5vwQAWD1gfCg== + version "3.7.17" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.17.tgz#1d2538729fd8ef138aa301a7cf62704474e57b72" + integrity sha512-0EErSHEtKPNl5wgWikHJbKFAzJ/k11O0WO2QyqZSHpdxdAnw7UWHY4YiLbHCFG7lhrD+NTQ3Z/H9Jn4rcikoJA== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/context" "^0.7.0" "@wry/equality" "^0.5.0" - "@wry/trie" "^0.3.0" + "@wry/trie" "^0.4.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.16.1" + optimism "^0.16.2" prop-types "^15.7.2" response-iterator "^0.2.6" symbol-observable "^4.0.0" @@ -15503,13 +15503,6 @@ undici "^5.8.0" web-streams-polyfill "^3.2.0" -"@wry/context@^0.6.0": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2" - integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw== - dependencies: - tslib "^2.3.0" - "@wry/context@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.0.tgz#be88e22c0ddf62aeb0ae9f95c3d90932c619a5c8" @@ -15531,6 +15524,13 @@ dependencies: tslib "^2.3.0" +"@wry/trie@^0.4.0": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" + integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== + dependencies: + tslib "^2.3.0" + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -18850,9 +18850,9 @@ cypress-pipe@^2.0.0: integrity sha512-KW9s+bz4tFLucH3rBGfjW+Q12n7S4QpUSSyxiGrgPOfoHlbYWzAGB3H26MO0VTojqf9NVvfd5Kt0MH5XMgbfyg== cypress@^12.16.0: - version "12.17.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.1.tgz#777fdcceec4ecd642fc90795f5994853b6ea03f8" - integrity sha512-eKfBgO6t8waEyhegL4gxD7tcI6uTCGttu+ZU7y9Hq8BlpMztd7iLeIF4AJFAnbZH1xjX+wwgg4cRKFNSvv3VWQ== + version "12.17.2" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.2.tgz#040ac55de1e811f6e037d231a2869d5ab8c29c85" + integrity sha512-hxWAaWbqQBzzMuadSGSuQg5PDvIGOovm6xm0hIfpCVcORsCAj/gF2p0EvfnJ4f+jK2PCiDgP6D2eeE9/FK4Mjg== dependencies: "@cypress/request" "^2.88.11" "@cypress/xvfb" "^1.2.4" @@ -26900,12 +26900,12 @@ opentracing@^0.14.4: resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.7.tgz#25d472bd0296dc0b64d7b94cbc995219031428f5" integrity sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q== -optimism@^0.16.1: - version "0.16.1" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.1.tgz#7c8efc1f3179f18307b887e18c15c5b7133f6e7d" - integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg== +optimism@^0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" + integrity sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ== dependencies: - "@wry/context" "^0.6.0" + "@wry/context" "^0.7.0" "@wry/trie" "^0.3.0" optionator@^0.8.1: @@ -33224,9 +33224,9 @@ winston@^3.0.0: winston-transport "^4.5.0" word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== wordwrap@^1.0.0: version "1.0.0"