diff --git a/services/app-web/src/pages/CMSDashboard/RateReviewsDashboard/RateReviewsTable.tsx b/services/app-web/src/pages/CMSDashboard/RateReviewsDashboard/RateReviewsTable.tsx
index 02fad0109b..f63c375911 100644
--- a/services/app-web/src/pages/CMSDashboard/RateReviewsDashboard/RateReviewsTable.tsx
+++ b/services/app-web/src/pages/CMSDashboard/RateReviewsDashboard/RateReviewsTable.tsx
@@ -411,7 +411,7 @@ export const RateReviewsTable = ({
-
+
{reactTable.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => (
diff --git a/services/cypress/integration/cmsWorkflow/rateReview.spec.ts b/services/cypress/integration/cmsWorkflow/rateReview.spec.ts
index e69de29bb2..8866d1ab69 100644
--- a/services/cypress/integration/cmsWorkflow/rateReview.spec.ts
+++ b/services/cypress/integration/cmsWorkflow/rateReview.spec.ts
@@ -0,0 +1,91 @@
+import { HealthPlanFormDataType, packageName } from "../../../app-web/src/common-code/healthPlanFormDataType"
+import { base64ToDomain } from "../../../app-web/src/common-code/proto/healthPlanFormDataProto"
+import { HealthPlanPackage } from "../../gen/gqlClient"
+import { cmsUser, minnesotaStatePrograms, stateUser } from "../../utils/apollo-test-utils"
+
+describe('CMS user can view rate reviews', () => {
+ beforeEach(() => {
+ cy.stubFeatureFlags()
+ cy.interceptGraphQL()
+ })
+ // By default return lastest revision
+ const getFormData = (pkg: HealthPlanPackage, indx = 0): HealthPlanFormDataType => {
+ const latestRevision = pkg.revisions[indx].node
+ if (!latestRevision) {
+ throw new Error('no revisions found for package' + pkg.id)
+ }
+
+ const unwrapResult = base64ToDomain(latestRevision.formDataProto)
+ if (unwrapResult instanceof Error) {
+ throw unwrapResult
+ }
+
+ return unwrapResult
+ }
+
+
+ it('and navigate to a specific rate from the rates dashboard', () => {
+ cy.interceptFeatureFlags({
+ 'rates-db-refactor':true,
+ 'rate-reviews-dashboard': true
+ })
+
+ cy.apiAssignDivisionToCMSUser(cmsUser(), 'DMCO').then(() => {
+
+ // Create a new contract and rates submission with two attached rates
+ cy.apiCreateAndSubmitContractWithRates(stateUser()).then(
+ (pkg) => {
+ const submission = getFormData(pkg)
+ const submissionName = packageName(
+ pkg.stateCode,
+ submission.stateNumber,
+ submission.programIDs,
+ minnesotaStatePrograms
+ )
+ // Then check both rates in rate reviews table
+ cy.logInAsCMSUser({
+ initialURL: `/dashboard/rate-reviews`,
+ })
+ const rate1 = submission.rateInfos[0]
+ const rate2 = submission.rateInfos[1]
+ cy.get('table')
+ .findByRole('link', { name: rate1.rateCertificationName })
+ .should('exist')
+ cy.get('table')
+ .findByRole('link', { name: rate2.rateCertificationName })
+ .should('exist')
+
+ // click the first rate to navigate to rate summary page
+ cy.get('table')
+ .findByRole('link', { name: rate1.rateCertificationName })
+ .should('exist').click()
+ cy.url({ timeout: 10_000 }).should('contain',rate1.id)
+ cy.findByRole('heading', {
+ name: `${rate1.rateCertificationName}`,
+ }).should('exist')
+ cy.findByText('Rate certification type').should('exist').siblings('dd').should('have.text', 'New rate certification')
+ cy.findByText('Rating period').should('exist').siblings('dd').should('have.text', '06/01/2025 to 05/30/2026')
+ cy.findByText('Date certified').should('exist').siblings('dd').should('have.text', '04/15/2025')
+ cy.findByText('Submission this rate was submitted with').should('exist').siblings('dd').should('have.text', submissionName)
+ cy.findByText('Certifying actuary').should('exist').siblings('dd').should('have.text', 'actuary1test titleemail@example.comMercer')
+ // cy.findByText('Download all rate documents').should('exist')
+ cy.findByRole('table', {
+ name: 'Rate certification',
+ }).should('exist')
+ cy.findByText('rate1Document1.pdf').should('exist')
+ cy.findByRole('table', {
+ name: 'Rate supporting documents',
+ }).should('exist')
+ })
+ cy.findByText('rate1SupportingDocument1.pdf').should('exist')
+
+ // Go back to dashboard and check both rates in the table
+ // check the dashboard has the columns we expect
+ cy.findByText('Back to dashboard').should('exist').click()
+ cy.url({ timeout: 10_000 }).should('contain', 'rate-reviews')
+ cy.findByText('Rate reviews').should('exist')
+ cy.get('thead').should('have.attr', 'data-testid', 'rate-reviews-table').should('be.visible') // can't put id on table itself because data attributes not passing through in react-uswds component
+
+ })
+ })
+})
diff --git a/services/cypress/support/apiCommands.ts b/services/cypress/support/apiCommands.ts
index ccd2ca8f31..e19fa65f83 100644
--- a/services/cypress/support/apiCommands.ts
+++ b/services/cypress/support/apiCommands.ts
@@ -18,6 +18,7 @@ import {
DivisionType,
adminUser,
contractOnlyData,
+ contractAndRatesData,
newSubmissionInput,
CMSUserType,
} from '../utils/apollo-test-utils'
@@ -71,6 +72,53 @@ const createAndSubmitContractOnlyPackage = async (
return submission.data.submitHealthPlanPackage.pkg
}
+const createAndSubmitContractWithRates = async (
+ apolloClient: ApolloClient
+): Promise => {
+ const newSubmission1 = await apolloClient.mutate({
+ mutation: CreateHealthPlanPackageDocument,
+ variables: {
+ input: newSubmissionInput({submissionType: 'CONTRACT_AND_RATES'}),
+ },
+ })
+ const pkg1 = newSubmission1.data.createHealthPlanPackage.pkg
+ const pkg1FirstRev = pkg1.revisions[0].node
+
+ const formData1 = base64ToDomain(pkg1FirstRev.formDataProto)
+ if (formData1 instanceof Error) {
+ throw new Error(formData1.message)
+ }
+
+ const fullFormData1 = {
+ ...formData1,
+ ...contractAndRatesData(),
+ }
+
+ const formDataProto = domainToBase64(fullFormData1)
+
+ await apolloClient.mutate({
+ mutation: UpdateHealthPlanFormDataDocument,
+ variables: {
+ input: {
+ healthPlanFormData: formDataProto,
+ pkgID: pkg1.id,
+ },
+ },
+ })
+
+ const submission1 = await apolloClient.mutate({
+ mutation: SubmitHealthPlanPackageDocument,
+ variables: {
+ input: {
+ pkgID: pkg1.id,
+ submittedReason: 'Submit package for Rates Dashboard tests',
+ },
+ },
+ })
+ return submission1.data.submitHealthPlanPackage.pkg
+}
+
+
const assignCmsDivision = async (
apolloClient: ApolloClient,
cmsUser: CMSUserType,
@@ -127,6 +175,19 @@ Cypress.Commands.add(
)
)
+
+Cypress.Commands.add(
+ 'apiCreateAndSubmitContractWithRates',
+ (stateUser): Cypress.Chainable =>
+ cy.task('readGraphQLSchema').then((schema) =>
+ apolloClientWrapper(
+ schema,
+ stateUser,
+ createAndSubmitContractWithRates
+ )
+ )
+)
+
Cypress.Commands.add(
'apiAssignDivisionToCMSUser',
(cmsUser, division): Cypress.Chainable =>
diff --git a/services/cypress/support/commands.ts b/services/cypress/support/commands.ts
index 14d9bdd4b2..484c339c30 100644
--- a/services/cypress/support/commands.ts
+++ b/services/cypress/support/commands.ts
@@ -50,6 +50,7 @@ Cypress.Commands.add('interceptGraphQL', () => {
aliasQuery(req, 'fetchHealthPlanPackage')
aliasQuery(req, 'fetchHealthPlanPackageWithQuestions')
aliasQuery(req, 'indexHealthPlanPackages')
+ aliasQuery(req, 'indexRates')
aliasMutation(req, 'createHealthPlanPackage')
aliasMutation(req, 'updateHealthPlanFormData')
aliasMutation(req, 'submitHealthPlanPackage')
diff --git a/services/cypress/support/index.ts b/services/cypress/support/index.ts
index 5764d2de93..f1391d3cd1 100644
--- a/services/cypress/support/index.ts
+++ b/services/cypress/support/index.ts
@@ -101,6 +101,7 @@ declare global {
}): void
apiCreateAndSubmitContractOnlySubmission(stateUser: StateUserType): Cypress.Chainable
+ apiCreateAndSubmitContractWithRates(stateUser: StateUserType): Cypress.Chainable
apiAssignDivisionToCMSUser(cmsUser: CMSUserType, division: DivisionType): Cypress.Chainable
interceptGraphQL(): void
diff --git a/services/cypress/support/loginCommands.ts b/services/cypress/support/loginCommands.ts
index 8024db89a2..6b2e5c2ab9 100644
--- a/services/cypress/support/loginCommands.ts
+++ b/services/cypress/support/loginCommands.ts
@@ -64,11 +64,15 @@ Cypress.Commands.add(
cy.wait('@fetchCurrentUserQuery', { timeout: 20_000 })
if (initialURL?.includes('submissions')) {
cy.wait('@fetchHealthPlanPackageWithQuestionsQuery', { timeout: 20_000 }) // for cases where CMs user goes to specific submission on login, likely from email link
+ } else if (initialURL?.includes('rate-reviews')) {
+ cy.wait('@indexRatesQuery', { timeout: 80_000 })
+ cy.findByTestId('cms-dashboard-page',{timeout: 10_000 }).should('exist')
+ cy.findByRole('heading', {name: /rate reviews/}).should('exist')
} else {
- // Default behavior on login is to go to CMS dashboard
+ // Default behavior on login is to go to CMS dashboard submissions
cy.wait('@indexHealthPlanPackagesQuery', { timeout: 80_000 })
cy.findByTestId('cms-dashboard-page',{timeout: 10_000 }).should('exist')
- cy.findByRole('heading', {name: 'Submissions'}).should('exist')
+ cy.findByRole('heading', {name: /Submissions/}).should('exist')
}
}
)
diff --git a/services/cypress/utils/apollo-test-utils.ts b/services/cypress/utils/apollo-test-utils.ts
index 4e0e11cc67..6fb2f85fc3 100644
--- a/services/cypress/utils/apollo-test-utils.ts
+++ b/services/cypress/utils/apollo-test-utils.ts
@@ -1,4 +1,5 @@
import { AxiosResponse } from 'axios'
+import { v4 as uuidv4 } from 'uuid'
import {
ApolloClient,
DocumentNode,
@@ -39,6 +40,30 @@ type DivisionType = 'DMCO' | 'DMCP' | 'OACT'
type UserType = StateUserType | AdminUserType | CMSUserType
+// programs for state used in tests
+const minnesotaStatePrograms =[
+ {
+ "id": "abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce",
+ "fullName": "Special Needs Basic Care",
+ "name": "SNBC"
+ },
+ {
+ "id": "d95394e5-44d1-45df-8151-1cc1ee66f100",
+ "fullName": "Prepaid Medical Assistance Program",
+ "name": "PMAP"
+ },
+ {
+ "id": "ea16a6c0-5fc6-4df8-adac-c627e76660ab",
+ "fullName": "Minnesota Senior Care Plus ",
+ "name": "MSC+"
+ },
+ {
+ "id": "3fd36500-bf2c-47bc-80e8-e7aa417184c5",
+ "fullName": "Minnesota Senior Health Options",
+ "name": "MSHO"
+ }
+]
+
const contractOnlyData = (): Partial=> ({
stateContacts: [
{
@@ -77,15 +102,123 @@ const contractOnlyData = (): Partial=> ({
rateInfos: [],
})
-const newSubmissionInput = (): Partial => ({
- populationCovered: 'MEDICAID',
- programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
- submissionType: 'CONTRACT_ONLY',
- riskBasedContract: false,
- submissionDescription: 'Test Q&A',
- contractType: 'BASE',
+const contractAndRatesData = (): Partial=> ({
+ stateCode: 'MN',
+ stateContacts: [
+ {
+ name: 'Name',
+ titleRole: 'Title',
+ email: 'example@example.com',
+ },
+ ],
+ addtlActuaryContacts: [],
+ documents: [],
+ contractExecutionStatus: 'EXECUTED' as const,
+ contractDocuments: [
+ {
+ name: 'Contract Cert.pdf',
+ s3URL: 's3://local-uploads/1684382956834-Contract Cert.pdf/Contract Cert.pdf',
+ documentCategories: ['CONTRACT'],
+ sha256: 'abc123',
+ },
+ ],
+ contractDateStart: new Date('2023-05-01T00:00:00.000Z'),
+ contractDateEnd: new Date('2023-05-31T00:00:00.000Z'),
+ contractAmendmentInfo: {
+ modifiedProvisions: {
+ inLieuServicesAndSettings: false,
+ modifiedRiskSharingStrategy: false,
+ modifiedIncentiveArrangements: false,
+ modifiedWitholdAgreements: false,
+ modifiedStateDirectedPayments: false,
+ modifiedPassThroughPayments: false,
+ modifiedPaymentsForMentalDiseaseInstitutions: false,
+ modifiedNonRiskPaymentArrangements: false,
+ },
+ },
+ managedCareEntities: ['MCO'],
+ federalAuthorities: ['STATE_PLAN'],
+ rateInfos:[
+ {
+ id: uuidv4(),
+ rateType: 'NEW' as const,
+ rateDateStart: new Date(Date.UTC(2025, 5, 1)),
+ rateDateEnd: new Date(Date.UTC(2026, 4, 30)),
+ rateDateCertified: new Date(Date.UTC(2025, 3, 15)),
+ rateDocuments: [
+ {
+ name: 'rate1Document1.pdf',
+ s3URL: 'fakeS3URL',
+ sha256: 'fakesha',
+ documentCategories: ['RATES' as const],
+ },
+ ],
+ supportingDocuments: [ {
+ name: 'rate1SupportingDocument1.pdf',
+ s3URL: 'fakeS3URL',
+ sha256: 'fakesha',
+ documentCategories: ['RATES' as const],
+ }],
+ rateProgramIDs: [minnesotaStatePrograms[0].id],
+ actuaryContacts: [
+ {
+ name: 'actuary1',
+ titleRole: 'test title',
+ email: 'email@example.com',
+ actuarialFirm: 'MERCER' as const,
+ actuarialFirmOther: '',
+ },
+ ],
+ actuaryCommunicationPreference: 'OACT_TO_ACTUARY' as const,
+ packagesWithSharedRateCerts: [],
+ },
+ {
+ id: uuidv4(),
+ rateType: 'NEW' as const,
+ rateDateStart: new Date(Date.UTC(2030, 5, 1)),
+ rateDateEnd: new Date(Date.UTC(2036, 4, 30)),
+ rateDateCertified: new Date(Date.UTC(2035, 3, 15)),
+ rateDocuments: [
+ {
+ name: 'rate2Document1.pdf',
+ s3URL: 'fakeS3URL',
+ sha256: 'fakesha',
+ documentCategories: ['RATES' as const],
+ },
+ ],
+ supportingDocuments: [],
+ rateProgramIDs: [minnesotaStatePrograms[0].id],
+ actuaryContacts: [
+ {
+ name: 'actuary2',
+ titleRole: 'test title',
+ email: 'email@example.com',
+ actuarialFirm: 'MERCER' as const,
+ actuarialFirmOther: '',
+ },
+ ],
+ actuaryCommunicationPreference: 'OACT_TO_ACTUARY' as const,
+ packagesWithSharedRateCerts: [],
+ },
+ ]
+
+
})
+const newSubmissionInput = (overrides?: Partial ): Partial => {
+ return Object.assign(
+ {
+ populationCovered: 'MEDICAID',
+ programIDs: [minnesotaStatePrograms[0].id],
+ submissionType: 'CONTRACT_ONLY',
+ riskBasedContract: false,
+ submissionDescription: 'Test Q&A',
+ contractType: 'BASE'
+ },
+ overrides
+ )
+ }
+
const stateUser = ():StateUserType => ({
id: 'user1',
email: 'aang@example.com',
@@ -298,10 +431,12 @@ const apolloClientWrapper = async (
export {
apolloClientWrapper,
contractOnlyData,
+ contractAndRatesData,
newSubmissionInput,
cmsUser,
adminUser,
stateUser,
+ minnesotaStatePrograms
}
export type {
StateUserType,