From efa91276dca4391b7a0d4568c700519c0a64af99 Mon Sep 17 00:00:00 2001 From: Jez Barnsley Date: Thu, 23 Apr 2026 15:35:04 +0100 Subject: [PATCH 1/4] Handles component usage metrics --- src/api/forms/service/__stubs__/metrics.js | 48 +++++++++++-- src/api/forms/service/report-overview.js | 67 ++++++++++++++++++- src/api/forms/service/report-overview.test.js | 55 +++++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) diff --git a/src/api/forms/service/__stubs__/metrics.js b/src/api/forms/service/__stubs__/metrics.js index d72b7d83..8802f041 100644 --- a/src/api/forms/service/__stubs__/metrics.js +++ b/src/api/forms/service/__stubs__/metrics.js @@ -23,7 +23,17 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-1-title', status: 'draft' }, - featureCounts: {}, + featureCounts: { + features: ['Sections'], + formStructure: { + conditions: 0, + pages: 1, + questionTypes: 0, + questions: 0, + sections: 1 + }, + questionTypes: {} + }, submissionsCount: 0, updatedAt: timestamp }, @@ -37,7 +47,17 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-2-title', status: 'draft' }, - featureCounts: {}, + featureCounts: { + features: ['Sections'], + formStructure: { + conditions: 0, + pages: 1, + questionTypes: 0, + questions: 0, + sections: 1 + }, + questionTypes: {} + }, submissionsCount: 0, updatedAt: timestamp }, @@ -51,7 +71,17 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-3-title', status: 'draft' }, - featureCounts: {}, + featureCounts: { + features: ['Sections'], + formStructure: { + conditions: 0, + pages: 1, + questionTypes: 0, + questions: 0, + sections: 1 + }, + questionTypes: {} + }, submissionsCount: 0, updatedAt: timestamp } @@ -67,7 +97,17 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-2-title', status: 'live' }, - featureCounts: {}, + featureCounts: { + features: ['Sections'], + formStructure: { + conditions: 0, + pages: 1, + questionTypes: 0, + questions: 0, + sections: 1 + }, + questionTypes: {} + }, submissionsCount: 0, updatedAt: timestamp } diff --git a/src/api/forms/service/report-overview.js b/src/api/forms/service/report-overview.js index fef36c9c..60112d19 100644 --- a/src/api/forms/service/report-overview.js +++ b/src/api/forms/service/report-overview.js @@ -131,7 +131,7 @@ export function collectOverviewMetrics(metadata, definition, definitionType) { formId: metadata.id, formStatus: metadata.live ? FormStatus.Live : FormStatus.Draft, summaryMetrics: calcSummaryMetrics(metadata, definition, definitionType), - featureCounts: {}, + featureCounts: calcFeatureMetrics(definition), submissionsCount: 0, updatedAt: new Date() } @@ -157,6 +157,69 @@ export function calcSummaryMetrics(metadata, definition, definitionType) { }) } +/** + * @param {FormDefinition} definition + */ +export function calcFeatureMetrics(definition) { + const allComponents = /** @type {ComponentDef[]} */ ([]) + for (const page of definition.pages) { + if (hasComponentsEvenIfNoNext(page)) { + allComponents.push(...page.components) + } + } + const questionTypes = getQuestionTypeCounts(allComponents) + return { + questionTypes: Object.fromEntries(questionTypes), + features: getComponentUsageFeatureList(definition), + formStructure: getFormStructureCounts(definition, questionTypes) + } +} + +/** + * @param {ComponentDef[]} components + */ +export function getQuestionTypeCounts(components) { + const componentCounts = /** @type {Map} */ (new Map()) + for (const component of components) { + const count = componentCounts.get(component.type) ?? 0 + componentCounts.set(component.type, count + 1) + } + return componentCounts +} + +/** + * @param {FormDefinition} definition + */ +export function getComponentUsageFeatureList(definition) { + const features = getFeatureList(definition) + if (definition.sections.length > 0) { + features.push('Sections') + } + if (definition.conditions.length > 0) { + features.push('Conditional logic') + } + return features +} + +/** + * @param {FormDefinition} definition + * @param {Map} questionTypes + */ +export function getFormStructureCounts(definition, questionTypes) { + let numOfQuestions = 0 + questionTypes.forEach((value) => { + numOfQuestions += value + }) + + return { + pages: definition.pages.length, + questions: numOfQuestions, + sections: definition.sections.length, + conditions: definition.conditions.length, + questionTypes: questionTypes.size + } +} + /** * @param {FormDefinition} definition */ @@ -201,5 +264,5 @@ export function getUniqueComponentTypes(definition) { /** * @import { ClientSession } from 'mongodb' - * @import { FormDefinition, FormMetadata } from '@defra/forms-model' + * @import { ComponentDef, FormDefinition, FormMetadata } from '@defra/forms-model' */ diff --git a/src/api/forms/service/report-overview.test.js b/src/api/forms/service/report-overview.test.js index 2539e737..e55f4ec6 100644 --- a/src/api/forms/service/report-overview.test.js +++ b/src/api/forms/service/report-overview.test.js @@ -20,6 +20,7 @@ import * as formDefinition from '~/src/api/forms/repositories/form-definition-re import { getMetadataCursorOfAllForms } from '~/src/api/forms/repositories/form-metadata-repository.js' import { getExpectedOverviewMetrics } from '~/src/api/forms/service/__stubs__/metrics.js' import { + calcFeatureMetrics, generateReportOverview, getDefinitionIfExists, getFeatureList, @@ -243,6 +244,60 @@ describe('report-overview', () => { 'Declarations' ]) }) + + describe('calcFeatureMetrics', () => { + it('should return calculated metrics', () => { + const summaryPage = buildSummaryPage({ + // @ts-expect-error - forcing the controller type + controller: ControllerType.SummaryWithConfirmationEmail + }) + const questionPageId = 'd9c99072-d25d-4688-ab7d-3822cffe802b' + const questionPage = buildQuestionPage({ + id: questionPageId, + components: [ + buildTextFieldComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildRadioComponent(), + buildDeclarationFieldComponent() + ] + }) + const fileUploadPage = buildFileUploadPage() + const paymentPage = buildQuestionPage({ + components: [buildPaymentComponent()] + }) + + const definition = buildDefinition({ + pages: [questionPage, fileUploadPage, paymentPage, summaryPage] + }) + expect(calcFeatureMetrics(definition)).toEqual({ + features: [ + 'File upload', + 'Email confirmation', + 'GOV.UK Pay', + 'Declarations', + 'Sections' + ], + formStructure: { + conditions: 0, + pages: 4, + questionTypes: 6, + questions: 9, + sections: 1 + }, + questionTypes: { + CheckboxesField: 2, + DeclarationField: 1, + FileUploadField: 1, + PaymentField: 1, + RadiosField: 1, + TextField: 3 + } + }) + }) + }) }) describe('getDefinitionIfExists', () => { From 7c9e54555afb01324772cca1e36d48a7bd478b7a Mon Sep 17 00:00:00 2001 From: Jez Barnsley Date: Thu, 23 Apr 2026 15:52:37 +0100 Subject: [PATCH 2/4] Extra tests --- src/api/forms/service/report-overview.test.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/api/forms/service/report-overview.test.js b/src/api/forms/service/report-overview.test.js index e55f4ec6..5e0e06ec 100644 --- a/src/api/forms/service/report-overview.test.js +++ b/src/api/forms/service/report-overview.test.js @@ -22,8 +22,10 @@ import { getExpectedOverviewMetrics } from '~/src/api/forms/service/__stubs__/me import { calcFeatureMetrics, generateReportOverview, + getComponentUsageFeatureList, getDefinitionIfExists, getFeatureList, + getQuestionTypeCounts, getUniqueComponentTypes } from '~/src/api/forms/service/report-overview.js' import { client } from '~/src/mongo.js' @@ -298,6 +300,70 @@ describe('report-overview', () => { }) }) }) + + describe('getQuestionTypeCounts', () => { + it('should return counts', () => { + const components = [ + buildTextFieldComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildRadioComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildRadioComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildDeclarationFieldComponent() + ] + expect(Object.fromEntries(getQuestionTypeCounts(components))).toEqual({ + CheckboxesField: 3, + DeclarationField: 1, + RadiosField: 2, + TextField: 5 + }) + }) + }) + + describe('get component usage features', () => { + it('should return list of features', () => { + const summaryPage = buildSummaryPage({ + // @ts-expect-error - forcing the controller type + controller: ControllerType.SummaryWithConfirmationEmail + }) + const questionPageId = 'd9c99072-d25d-4688-ab7d-3822cffe802b' + const questionPage = buildQuestionPage({ + id: questionPageId, + components: [ + buildTextFieldComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildRadioComponent(), + buildDeclarationFieldComponent() + ] + }) + const fileUploadPage = buildFileUploadPage() + const paymentPage = buildQuestionPage({ + components: [buildPaymentComponent()] + }) + + const definition = buildDefinition({ + pages: [questionPage, fileUploadPage, paymentPage, summaryPage], + // @ts-expect-error - partial mock of a condition + conditions: [{}] + }) + expect(getComponentUsageFeatureList(definition)).toEqual([ + 'File upload', + 'Email confirmation', + 'GOV.UK Pay', + 'Declarations', + 'Sections', + 'Conditional logic' + ]) + }) + }) }) describe('getDefinitionIfExists', () => { From e505db272a8dac20f725ec01c370572d26505c42 Mon Sep 17 00:00:00 2001 From: Jez Barnsley Date: Fri, 24 Apr 2026 13:37:03 +0100 Subject: [PATCH 3/4] Handles component usage --- src/api/forms/service/__stubs__/metrics.js | 8 ++--- src/api/forms/service/report-overview.js | 10 ++++-- src/api/forms/service/report-overview.test.js | 32 +++++++++---------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/api/forms/service/__stubs__/metrics.js b/src/api/forms/service/__stubs__/metrics.js index 8802f041..e1e92ec8 100644 --- a/src/api/forms/service/__stubs__/metrics.js +++ b/src/api/forms/service/__stubs__/metrics.js @@ -24,7 +24,7 @@ export function getExpectedOverviewMetrics(timestamp) { status: 'draft' }, featureCounts: { - features: ['Sections'], + features: { Sections: 1 }, formStructure: { conditions: 0, pages: 1, @@ -48,7 +48,7 @@ export function getExpectedOverviewMetrics(timestamp) { status: 'draft' }, featureCounts: { - features: ['Sections'], + features: { Sections: 1 }, formStructure: { conditions: 0, pages: 1, @@ -72,7 +72,7 @@ export function getExpectedOverviewMetrics(timestamp) { status: 'draft' }, featureCounts: { - features: ['Sections'], + features: { Sections: 1 }, formStructure: { conditions: 0, pages: 1, @@ -98,7 +98,7 @@ export function getExpectedOverviewMetrics(timestamp) { status: 'live' }, featureCounts: { - features: ['Sections'], + features: { Sections: 1 }, formStructure: { conditions: 0, pages: 1, diff --git a/src/api/forms/service/report-overview.js b/src/api/forms/service/report-overview.js index 60112d19..4d72dc74 100644 --- a/src/api/forms/service/report-overview.js +++ b/src/api/forms/service/report-overview.js @@ -170,7 +170,7 @@ export function calcFeatureMetrics(definition) { const questionTypes = getQuestionTypeCounts(allComponents) return { questionTypes: Object.fromEntries(questionTypes), - features: getComponentUsageFeatureList(definition), + features: getComponentUsageFeatureCounts(definition), formStructure: getFormStructureCounts(definition, questionTypes) } } @@ -190,7 +190,7 @@ export function getQuestionTypeCounts(components) { /** * @param {FormDefinition} definition */ -export function getComponentUsageFeatureList(definition) { +export function getComponentUsageFeatureCounts(definition) { const features = getFeatureList(definition) if (definition.sections.length > 0) { features.push('Sections') @@ -198,7 +198,11 @@ export function getComponentUsageFeatureList(definition) { if (definition.conditions.length > 0) { features.push('Conditional logic') } - return features + const featureResult = /** @type {Record} */ ({}) + features.forEach((f) => { + featureResult[f] = 1 + }) + return featureResult } /** diff --git a/src/api/forms/service/report-overview.test.js b/src/api/forms/service/report-overview.test.js index 5e0e06ec..d96caad1 100644 --- a/src/api/forms/service/report-overview.test.js +++ b/src/api/forms/service/report-overview.test.js @@ -22,7 +22,7 @@ import { getExpectedOverviewMetrics } from '~/src/api/forms/service/__stubs__/me import { calcFeatureMetrics, generateReportOverview, - getComponentUsageFeatureList, + getComponentUsageFeatureCounts, getDefinitionIfExists, getFeatureList, getQuestionTypeCounts, @@ -275,13 +275,13 @@ describe('report-overview', () => { pages: [questionPage, fileUploadPage, paymentPage, summaryPage] }) expect(calcFeatureMetrics(definition)).toEqual({ - features: [ - 'File upload', - 'Email confirmation', - 'GOV.UK Pay', - 'Declarations', - 'Sections' - ], + features: { + 'File upload': 1, + 'Email confirmation': 1, + 'GOV.UK Pay': 1, + Declarations: 1, + Sections: 1 + }, formStructure: { conditions: 0, pages: 4, @@ -354,14 +354,14 @@ describe('report-overview', () => { // @ts-expect-error - partial mock of a condition conditions: [{}] }) - expect(getComponentUsageFeatureList(definition)).toEqual([ - 'File upload', - 'Email confirmation', - 'GOV.UK Pay', - 'Declarations', - 'Sections', - 'Conditional logic' - ]) + expect(getComponentUsageFeatureCounts(definition)).toEqual({ + 'File upload': 1, + 'Email confirmation': 1, + 'GOV.UK Pay': 1, + Declarations: 1, + Sections: 1, + 'Conditional logic': 1 + }) }) }) }) From 524730fd066124eae7078c314c67a12db39f41a1 Mon Sep 17 00:00:00 2001 From: Jez Barnsley Date: Fri, 24 Apr 2026 14:08:12 +0100 Subject: [PATCH 4/4] Rework after Copilot review --- src/api/forms/service/__stubs__/metrics.js | 12 +- src/api/forms/service/report-overview.js | 14 +- src/api/forms/service/report-overview.test.js | 246 ++++++++++-------- 3 files changed, 155 insertions(+), 117 deletions(-) diff --git a/src/api/forms/service/__stubs__/metrics.js b/src/api/forms/service/__stubs__/metrics.js index e1e92ec8..74e2c7c9 100644 --- a/src/api/forms/service/__stubs__/metrics.js +++ b/src/api/forms/service/__stubs__/metrics.js @@ -23,7 +23,7 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-1-title', status: 'draft' }, - featureCounts: { + featureMetrics: { features: { Sections: 1 }, formStructure: { conditions: 0, @@ -47,7 +47,7 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-2-title', status: 'draft' }, - featureCounts: { + featureMetrics: { features: { Sections: 1 }, formStructure: { conditions: 0, @@ -71,14 +71,14 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-3-title', status: 'draft' }, - featureCounts: { - features: { Sections: 1 }, + featureMetrics: { + features: {}, formStructure: { conditions: 0, pages: 1, questionTypes: 0, questions: 0, - sections: 1 + sections: 0 }, questionTypes: {} }, @@ -97,7 +97,7 @@ export function getExpectedOverviewMetrics(timestamp) { slug: 'form-2-title', status: 'live' }, - featureCounts: { + featureMetrics: { features: { Sections: 1 }, formStructure: { conditions: 0, diff --git a/src/api/forms/service/report-overview.js b/src/api/forms/service/report-overview.js index 4d72dc74..ae345bf2 100644 --- a/src/api/forms/service/report-overview.js +++ b/src/api/forms/service/report-overview.js @@ -131,7 +131,7 @@ export function collectOverviewMetrics(metadata, definition, definitionType) { formId: metadata.id, formStatus: metadata.live ? FormStatus.Live : FormStatus.Draft, summaryMetrics: calcSummaryMetrics(metadata, definition, definitionType), - featureCounts: calcFeatureMetrics(definition), + featureMetrics: calcFeatureMetrics(definition), submissionsCount: 0, updatedAt: new Date() } @@ -170,7 +170,7 @@ export function calcFeatureMetrics(definition) { const questionTypes = getQuestionTypeCounts(allComponents) return { questionTypes: Object.fromEntries(questionTypes), - features: getComponentUsageFeatureCounts(definition), + features: getComponentUsageFeatureMetrics(definition), formStructure: getFormStructureCounts(definition, questionTypes) } } @@ -190,12 +190,12 @@ export function getQuestionTypeCounts(components) { /** * @param {FormDefinition} definition */ -export function getComponentUsageFeatureCounts(definition) { +export function getComponentUsageFeatureMetrics(definition) { const features = getFeatureList(definition) - if (definition.sections.length > 0) { + if (definition.pages.some((p) => p.section)) { features.push('Sections') } - if (definition.conditions.length > 0) { + if (definition.pages.some((p) => p.condition)) { features.push('Conditional logic') } const featureResult = /** @type {Record} */ ({}) @@ -218,8 +218,8 @@ export function getFormStructureCounts(definition, questionTypes) { return { pages: definition.pages.length, questions: numOfQuestions, - sections: definition.sections.length, - conditions: definition.conditions.length, + sections: definition.pages.filter((p) => p.section).length, + conditions: definition.pages.filter((p) => p.condition).length, questionTypes: questionTypes.size } } diff --git a/src/api/forms/service/report-overview.test.js b/src/api/forms/service/report-overview.test.js index d96caad1..140a57b5 100644 --- a/src/api/forms/service/report-overview.test.js +++ b/src/api/forms/service/report-overview.test.js @@ -22,7 +22,7 @@ import { getExpectedOverviewMetrics } from '~/src/api/forms/service/__stubs__/me import { calcFeatureMetrics, generateReportOverview, - getComponentUsageFeatureCounts, + getComponentUsageFeatureMetrics, getDefinitionIfExists, getFeatureList, getQuestionTypeCounts, @@ -116,12 +116,22 @@ describe('report-overview', () => { // @ts-expect-error - resolves to an async iterator like FindCursor .mockReturnValueOnce(mockAsyncIterator) + const pageWithSection = /** @type {FormDefinition} */ ({ + pages: [{ section: 'abc' }] + }) + // Form 1 - draft and no live - jest.mocked(formDefinition.get).mockResolvedValueOnce(buildDefinition({})) + jest + .mocked(formDefinition.get) + .mockResolvedValueOnce(buildDefinition(pageWithSection)) // Form 2 - draft and live - jest.mocked(formDefinition.get).mockResolvedValueOnce(buildDefinition({})) - jest.mocked(formDefinition.get).mockResolvedValueOnce(buildDefinition({})) + jest + .mocked(formDefinition.get) + .mockResolvedValueOnce(buildDefinition(pageWithSection)) + jest + .mocked(formDefinition.get) + .mockResolvedValueOnce(buildDefinition(pageWithSection)) // Form 3 - draft and no live jest.mocked(formDefinition.get).mockResolvedValueOnce(buildDefinition({})) @@ -246,122 +256,146 @@ describe('report-overview', () => { 'Declarations' ]) }) + }) - describe('calcFeatureMetrics', () => { - it('should return calculated metrics', () => { - const summaryPage = buildSummaryPage({ - // @ts-expect-error - forcing the controller type - controller: ControllerType.SummaryWithConfirmationEmail - }) - const questionPageId = 'd9c99072-d25d-4688-ab7d-3822cffe802b' - const questionPage = buildQuestionPage({ - id: questionPageId, - components: [ - buildTextFieldComponent(), - buildTextFieldComponent(), - buildCheckboxComponent(), - buildTextFieldComponent(), - buildCheckboxComponent(), - buildRadioComponent(), - buildDeclarationFieldComponent() - ] - }) - const fileUploadPage = buildFileUploadPage() - const paymentPage = buildQuestionPage({ - components: [buildPaymentComponent()] - }) - - const definition = buildDefinition({ - pages: [questionPage, fileUploadPage, paymentPage, summaryPage] - }) - expect(calcFeatureMetrics(definition)).toEqual({ - features: { - 'File upload': 1, - 'Email confirmation': 1, - 'GOV.UK Pay': 1, - Declarations: 1, - Sections: 1 - }, - formStructure: { - conditions: 0, - pages: 4, - questionTypes: 6, - questions: 9, - sections: 1 - }, - questionTypes: { - CheckboxesField: 2, - DeclarationField: 1, - FileUploadField: 1, - PaymentField: 1, - RadiosField: 1, - TextField: 3 - } - }) + describe('calcFeatureMetrics', () => { + it('should return calculated metrics', () => { + const summaryPage = buildSummaryPage({ + // @ts-expect-error - forcing the controller type + controller: ControllerType.SummaryWithConfirmationEmail }) - }) - - describe('getQuestionTypeCounts', () => { - it('should return counts', () => { - const components = [ + const questionPageId = 'd9c99072-d25d-4688-ab7d-3822cffe802b' + const questionPage = buildQuestionPage({ + id: questionPageId, + components: [ buildTextFieldComponent(), buildTextFieldComponent(), buildCheckboxComponent(), buildTextFieldComponent(), - buildRadioComponent(), buildCheckboxComponent(), - buildTextFieldComponent(), buildRadioComponent(), - buildCheckboxComponent(), - buildTextFieldComponent(), buildDeclarationFieldComponent() ] - expect(Object.fromEntries(getQuestionTypeCounts(components))).toEqual({ - CheckboxesField: 3, - DeclarationField: 1, - RadiosField: 2, - TextField: 5 - }) }) - }) - - describe('get component usage features', () => { - it('should return list of features', () => { - const summaryPage = buildSummaryPage({ - // @ts-expect-error - forcing the controller type - controller: ControllerType.SummaryWithConfirmationEmail - }) - const questionPageId = 'd9c99072-d25d-4688-ab7d-3822cffe802b' - const questionPage = buildQuestionPage({ - id: questionPageId, - components: [ - buildTextFieldComponent(), - buildTextFieldComponent(), - buildCheckboxComponent(), - buildTextFieldComponent(), - buildCheckboxComponent(), - buildRadioComponent(), - buildDeclarationFieldComponent() - ] - }) - const fileUploadPage = buildFileUploadPage() - const paymentPage = buildQuestionPage({ - components: [buildPaymentComponent()] - }) + const fileUploadPage = buildFileUploadPage() + const paymentPage = buildQuestionPage({ + components: [buildPaymentComponent()] + }) + const sectionPage1 = buildQuestionPage({ + section: 'some-section-id1' + }) + const sectionPage2 = buildQuestionPage({ + section: 'some-section-id2' + }) - const definition = buildDefinition({ - pages: [questionPage, fileUploadPage, paymentPage, summaryPage], - // @ts-expect-error - partial mock of a condition - conditions: [{}] - }) - expect(getComponentUsageFeatureCounts(definition)).toEqual({ + const definition = buildDefinition({ + pages: [ + questionPage, + fileUploadPage, + sectionPage1, + sectionPage2, + paymentPage, + summaryPage + ] + }) + expect(calcFeatureMetrics(definition)).toEqual({ + features: { 'File upload': 1, 'Email confirmation': 1, 'GOV.UK Pay': 1, Declarations: 1, - Sections: 1, - 'Conditional logic': 1 - }) + Sections: 1 + }, + formStructure: { + conditions: 0, + pages: 6, + questionTypes: 6, + questions: 9, + sections: 2 + }, + questionTypes: { + CheckboxesField: 2, + DeclarationField: 1, + FileUploadField: 1, + PaymentField: 1, + RadiosField: 1, + TextField: 3 + } + }) + }) + }) + + describe('getQuestionTypeCounts', () => { + it('should return counts', () => { + const components = [ + buildTextFieldComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildRadioComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildRadioComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildDeclarationFieldComponent() + ] + expect(Object.fromEntries(getQuestionTypeCounts(components))).toEqual({ + CheckboxesField: 3, + DeclarationField: 1, + RadiosField: 2, + TextField: 5 + }) + }) + }) + + describe('get component usage features', () => { + it('should return list of features', () => { + const summaryPage = buildSummaryPage({ + // @ts-expect-error - forcing the controller type + controller: ControllerType.SummaryWithConfirmationEmail + }) + const questionPageId = 'd9c99072-d25d-4688-ab7d-3822cffe802b' + const questionPage = buildQuestionPage({ + id: questionPageId, + components: [ + buildTextFieldComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildTextFieldComponent(), + buildCheckboxComponent(), + buildRadioComponent(), + buildDeclarationFieldComponent() + ] + }) + const fileUploadPage = buildFileUploadPage() + const paymentPage = buildQuestionPage({ + components: [buildPaymentComponent()] + }) + const conditionPage = buildQuestionPage({ + condition: 'some-condition-id' + }) + const sectionPage = buildQuestionPage({ + section: 'some-section-id' + }) + + const definition = buildDefinition({ + pages: [ + questionPage, + fileUploadPage, + paymentPage, + conditionPage, + sectionPage, + summaryPage + ] + }) + expect(getComponentUsageFeatureMetrics(definition)).toEqual({ + 'File upload': 1, + 'Email confirmation': 1, + 'GOV.UK Pay': 1, + Declarations: 1, + Sections: 1, + 'Conditional logic': 1 }) }) }) @@ -389,3 +423,7 @@ describe('report-overview', () => { }) }) }) + +/** + * @import { FormDefinition } from '@defra/forms-model' + */