VVT: Master library tables (7 catalogs), 500+ seed entries, process templates with instantiation, library API endpoints + 18 tests. Loeschfristen: Baseline catalog, compliance checks, profiling engine, HTML document generator, MkDocs documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
511 lines
20 KiB
TypeScript
511 lines
20 KiB
TypeScript
/**
|
|
* Integration Tests: Company Profile → Compliance Scope → VVT Generator
|
|
*
|
|
* Tests the complete data pipeline from Company Profile master data
|
|
* through the Compliance Scope Engine to VVT activity generation.
|
|
*/
|
|
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
prefillFromCompanyProfile,
|
|
exportToVVTAnswers,
|
|
getAutoFilledScoringAnswers,
|
|
SCOPE_QUESTION_BLOCKS,
|
|
} from '../compliance-scope-profiling'
|
|
import {
|
|
generateActivities,
|
|
PROFILING_QUESTIONS,
|
|
DEPARTMENT_DATA_CATEGORIES,
|
|
SCOPE_PREFILLED_VVT_QUESTIONS,
|
|
} from '../vvt-profiling'
|
|
import type { ScopeProfilingAnswer } from '../compliance-scope-types'
|
|
|
|
// Helper
|
|
function ans(questionId: string, value: unknown): ScopeProfilingAnswer {
|
|
return { questionId, value } as ScopeProfilingAnswer
|
|
}
|
|
|
|
// =============================================================================
|
|
// 1. Company Profile → Scope Prefill
|
|
// =============================================================================
|
|
|
|
describe('CompanyProfile → Scope prefill', () => {
|
|
it('prefills org_has_dsb when dpoName is set', () => {
|
|
const profile = { dpoName: 'Max Mustermann' } as any
|
|
const answers = prefillFromCompanyProfile(profile)
|
|
expect(answers.find((a) => a.questionId === 'org_has_dsb')?.value).toBe(true)
|
|
})
|
|
|
|
it('does NOT prefill org_has_dsb when dpoName is empty', () => {
|
|
const profile = { dpoName: '' } as any
|
|
const answers = prefillFromCompanyProfile(profile)
|
|
expect(answers.find((a) => a.questionId === 'org_has_dsb')).toBeUndefined()
|
|
})
|
|
|
|
it('maps offerings to prod_type correctly', () => {
|
|
const profile = { offerings: ['WebApp', 'SaaS', 'API'] } as any
|
|
const answers = prefillFromCompanyProfile(profile)
|
|
const prodType = answers.find((a) => a.questionId === 'prod_type')
|
|
expect(prodType?.value).toEqual(expect.arrayContaining(['webapp', 'saas', 'api']))
|
|
})
|
|
|
|
it('detects webshop in offerings', () => {
|
|
const profile = { offerings: ['Webshop'] } as any
|
|
const answers = prefillFromCompanyProfile(profile)
|
|
expect(answers.find((a) => a.questionId === 'prod_webshop')?.value).toBe(true)
|
|
})
|
|
|
|
it('returns empty array when profile has no relevant data', () => {
|
|
const profile = {} as any
|
|
const answers = prefillFromCompanyProfile(profile)
|
|
expect(answers).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('CompanyProfile → Scope scoring answers', () => {
|
|
it('maps employeeCount to org_employee_count', () => {
|
|
const profile = { employeeCount: '50-249' } as any
|
|
const answers = getAutoFilledScoringAnswers(profile)
|
|
expect(answers.find((a) => a.questionId === 'org_employee_count')?.value).toBe('50-249')
|
|
})
|
|
|
|
it('maps industry to org_industry', () => {
|
|
const profile = { industry: ['IT & Software', 'Finanzdienstleistungen'] } as any
|
|
const answers = getAutoFilledScoringAnswers(profile)
|
|
expect(answers.find((a) => a.questionId === 'org_industry')?.value).toBe(
|
|
'IT & Software, Finanzdienstleistungen'
|
|
)
|
|
})
|
|
|
|
it('maps annualRevenue to org_annual_revenue', () => {
|
|
const profile = { annualRevenue: '1-10M' } as any
|
|
const answers = getAutoFilledScoringAnswers(profile)
|
|
expect(answers.find((a) => a.questionId === 'org_annual_revenue')?.value).toBe('1-10M')
|
|
})
|
|
|
|
it('maps businessModel to org_business_model', () => {
|
|
const profile = { businessModel: 'B2B' } as any
|
|
const answers = getAutoFilledScoringAnswers(profile)
|
|
expect(answers.find((a) => a.questionId === 'org_business_model')?.value).toBe('B2B')
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 2. Scope → VVT Answer Mapping (exportToVVTAnswers)
|
|
// =============================================================================
|
|
|
|
describe('Scope → VVT answer export', () => {
|
|
it('maps scope questions with mapsToVVTQuestion property', () => {
|
|
// Block 9: dk_dept_hr maps to dept_hr_categories
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [
|
|
ans('dk_dept_hr', ['NAME', 'SALARY_DATA', 'HEALTH_DATA']),
|
|
]
|
|
const vvtAnswers = exportToVVTAnswers(scopeAnswers)
|
|
expect(vvtAnswers.dept_hr_categories).toEqual(['NAME', 'SALARY_DATA', 'HEALTH_DATA'])
|
|
})
|
|
|
|
it('maps multiple department data categories', () => {
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [
|
|
ans('dk_dept_hr', ['NAME', 'BANK_ACCOUNT']),
|
|
ans('dk_dept_finance', ['INVOICE_DATA', 'TAX_ID']),
|
|
ans('dk_dept_marketing', ['EMAIL', 'TRACKING_DATA']),
|
|
]
|
|
const vvtAnswers = exportToVVTAnswers(scopeAnswers)
|
|
expect(vvtAnswers.dept_hr_categories).toEqual(['NAME', 'BANK_ACCOUNT'])
|
|
expect(vvtAnswers.dept_finance_categories).toEqual(['INVOICE_DATA', 'TAX_ID'])
|
|
expect(vvtAnswers.dept_marketing_categories).toEqual(['EMAIL', 'TRACKING_DATA'])
|
|
})
|
|
|
|
it('ignores scope questions without mapsToVVTQuestion', () => {
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [
|
|
ans('vvt_has_vvt', true), // No mapsToVVTQuestion property
|
|
]
|
|
const vvtAnswers = exportToVVTAnswers(scopeAnswers)
|
|
expect(Object.keys(vvtAnswers)).toHaveLength(0)
|
|
})
|
|
|
|
it('handles empty scope answers', () => {
|
|
const vvtAnswers = exportToVVTAnswers([])
|
|
expect(vvtAnswers).toEqual({})
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 3. Scope → VVT Profiling Prefill
|
|
// Note: prefillFromScopeAnswers() uses dynamic require('./compliance-scope-profiling')
|
|
// which doesn't resolve in vitest. We test the same pipeline by calling
|
|
// exportToVVTAnswers() directly (which is what prefillFromScopeAnswers wraps).
|
|
// =============================================================================
|
|
|
|
describe('Scope → VVT Profiling Prefill (via exportToVVTAnswers)', () => {
|
|
it('converts scope answers to VVT ProfilingAnswers format', () => {
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [
|
|
ans('dk_dept_hr', ['NAME', 'HEALTH_DATA']),
|
|
ans('dk_dept_finance', ['BANK_ACCOUNT']),
|
|
]
|
|
const exported = exportToVVTAnswers(scopeAnswers)
|
|
// Same transformation as prefillFromScopeAnswers
|
|
const profiling: Record<string, unknown> = {}
|
|
for (const [key, value] of Object.entries(exported)) {
|
|
if (value !== undefined && value !== null) profiling[key] = value
|
|
}
|
|
expect(profiling.dept_hr_categories).toEqual(['NAME', 'HEALTH_DATA'])
|
|
expect(profiling.dept_finance_categories).toEqual(['BANK_ACCOUNT'])
|
|
})
|
|
|
|
it('filters out null/undefined values', () => {
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [ans('dk_dept_hr', null)]
|
|
const exported = exportToVVTAnswers(scopeAnswers)
|
|
const profiling: Record<string, unknown> = {}
|
|
for (const [key, value] of Object.entries(exported)) {
|
|
if (value !== undefined && value !== null) profiling[key] = value
|
|
}
|
|
expect(profiling.dept_hr_categories).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 4. VVT Generator — generateActivities
|
|
// =============================================================================
|
|
|
|
describe('generateActivities', () => {
|
|
it('always generates 4 IT baseline activities', () => {
|
|
const result = generateActivities({})
|
|
const names = result.generatedActivities.map((a) => a.name)
|
|
expect(result.generatedActivities.length).toBeGreaterThanOrEqual(4)
|
|
// IT baselines are always added
|
|
const itTemplates = result.generatedActivities.filter(
|
|
(a) => a.businessFunction === 'it_operations'
|
|
)
|
|
expect(itTemplates.length).toBeGreaterThanOrEqual(4)
|
|
})
|
|
|
|
it('triggers HR templates when dept_hr=true', () => {
|
|
const result = generateActivities({ dept_hr: true })
|
|
const hrActivities = result.generatedActivities.filter(
|
|
(a) => a.businessFunction === 'hr'
|
|
)
|
|
expect(hrActivities.length).toBeGreaterThanOrEqual(3) // mitarbeiter, gehalt, zeiterfassung
|
|
})
|
|
|
|
it('triggers finance templates when dept_finance=true', () => {
|
|
const result = generateActivities({ dept_finance: true })
|
|
const financeActivities = result.generatedActivities.filter(
|
|
(a) => a.businessFunction === 'finance'
|
|
)
|
|
expect(financeActivities.length).toBeGreaterThanOrEqual(2) // buchhaltung, zahlungsverkehr
|
|
})
|
|
|
|
it('enriches activities with US cloud third-country transfer', () => {
|
|
const result = generateActivities({ dept_hr: true, transfer_cloud_us: true })
|
|
// Every activity should have a US third-country transfer
|
|
for (const activity of result.generatedActivities) {
|
|
expect(activity.thirdCountryTransfers.some((t) => t.country === 'US')).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('adds HEALTH_DATA to HR activities when data_health=true', () => {
|
|
const result = generateActivities({ dept_hr: true, data_health: true })
|
|
const hrActivities = result.generatedActivities.filter(
|
|
(a) => a.businessFunction === 'hr'
|
|
)
|
|
expect(hrActivities.length).toBeGreaterThan(0)
|
|
for (const hr of hrActivities) {
|
|
expect(hr.personalDataCategories).toContain('HEALTH_DATA')
|
|
}
|
|
})
|
|
|
|
it('calculates Art. 30 Abs. 5 exemption correctly', () => {
|
|
// < 250 employees, no special categories → exempt
|
|
const result1 = generateActivities({ org_employees: 50 })
|
|
expect(result1.art30Abs5Exempt).toBe(true)
|
|
|
|
// >= 250 employees → not exempt
|
|
const result2 = generateActivities({ org_employees: 500 })
|
|
expect(result2.art30Abs5Exempt).toBe(false)
|
|
|
|
// < 250 but with special categories → not exempt
|
|
const result3 = generateActivities({ org_employees: 50, data_health: true })
|
|
expect(result3.art30Abs5Exempt).toBe(false)
|
|
})
|
|
|
|
it('generates unique VVT IDs for all activities', () => {
|
|
const result = generateActivities({
|
|
dept_hr: true,
|
|
dept_finance: true,
|
|
dept_sales: true,
|
|
dept_marketing: true,
|
|
})
|
|
const ids = result.generatedActivities.map((a) => a.vvtId)
|
|
const uniqueIds = new Set(ids)
|
|
expect(uniqueIds.size).toBe(ids.length)
|
|
})
|
|
|
|
it('calculates coverage score > 0 for template-generated activities', () => {
|
|
const result = generateActivities({ dept_hr: true })
|
|
expect(result.coverageScore).toBeGreaterThan(0)
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 5. Full Pipeline: Company Profile → Scope → VVT
|
|
// =============================================================================
|
|
|
|
describe('Full Pipeline: CompanyProfile → Scope → VVT Generation', () => {
|
|
// Helper: replicate what prefillFromScopeAnswers does (avoiding dynamic require)
|
|
function scopeToProfilingAnswers(
|
|
scopeAnswers: ScopeProfilingAnswer[]
|
|
): Record<string, string | string[] | number | boolean> {
|
|
const exported = exportToVVTAnswers(scopeAnswers)
|
|
const profiling: Record<string, string | string[] | number | boolean> = {}
|
|
for (const [key, value] of Object.entries(exported)) {
|
|
if (value !== undefined && value !== null) {
|
|
profiling[key] = value as string | string[] | number | boolean
|
|
}
|
|
}
|
|
return profiling
|
|
}
|
|
|
|
it('complete flow: profile with DSB → scope prefill → VVT generation', () => {
|
|
// Step 1: Company Profile
|
|
const profile = {
|
|
dpoName: 'Dr. Datenschutz',
|
|
employeeCount: '50-249',
|
|
industry: ['IT & Software'],
|
|
offerings: ['WebApp', 'SaaS'],
|
|
} as any
|
|
|
|
// Step 2: Prefill scope from profile
|
|
const profileAnswers = prefillFromCompanyProfile(profile)
|
|
const scoringAnswers = getAutoFilledScoringAnswers(profile)
|
|
|
|
// Simulate user answering scope questions + auto-prefilled from profile
|
|
const userAnswers: ScopeProfilingAnswer[] = [
|
|
// Block 8: departments
|
|
ans('vvt_departments', ['personal', 'finanzen', 'it']),
|
|
// Block 9: data categories per department
|
|
ans('dk_dept_hr', ['NAME', 'ADDRESS', 'SALARY_DATA', 'HEALTH_DATA']),
|
|
ans('dk_dept_finance', ['NAME', 'BANK_ACCOUNT', 'INVOICE_DATA', 'TAX_ID']),
|
|
ans('dk_dept_it', ['USER_ACCOUNTS', 'LOG_DATA', 'DEVICE_DATA']),
|
|
// Block 2: data types
|
|
ans('data_art9', true),
|
|
ans('data_minors', false),
|
|
]
|
|
|
|
const allScopeAnswers = [...profileAnswers, ...scoringAnswers, ...userAnswers]
|
|
|
|
// Step 3: Export to VVT format
|
|
const vvtAnswers = exportToVVTAnswers(allScopeAnswers)
|
|
expect(vvtAnswers.dept_hr_categories).toEqual([
|
|
'NAME',
|
|
'ADDRESS',
|
|
'SALARY_DATA',
|
|
'HEALTH_DATA',
|
|
])
|
|
expect(vvtAnswers.dept_finance_categories).toEqual([
|
|
'NAME',
|
|
'BANK_ACCOUNT',
|
|
'INVOICE_DATA',
|
|
'TAX_ID',
|
|
])
|
|
|
|
// Step 4: Prefill VVT profiling from scope (via direct export)
|
|
const profilingAnswers = scopeToProfilingAnswers(allScopeAnswers)
|
|
|
|
// Verify data survived the transformation
|
|
expect(profilingAnswers.dept_hr_categories).toEqual([
|
|
'NAME',
|
|
'ADDRESS',
|
|
'SALARY_DATA',
|
|
'HEALTH_DATA',
|
|
])
|
|
|
|
// Step 5: Generate VVT activities
|
|
// Add department triggers that match Block 8 selections
|
|
profilingAnswers.dept_hr = true
|
|
profilingAnswers.dept_finance = true
|
|
|
|
const result = generateActivities(profilingAnswers)
|
|
|
|
// Verify activities were generated
|
|
expect(result.generatedActivities.length).toBeGreaterThan(4) // 4 IT baseline + HR + Finance
|
|
|
|
// Verify HR activities exist
|
|
const hrActivities = result.generatedActivities.filter(
|
|
(a) => a.businessFunction === 'hr'
|
|
)
|
|
expect(hrActivities.length).toBeGreaterThanOrEqual(3)
|
|
|
|
// Verify finance activities exist
|
|
const financeActivities = result.generatedActivities.filter(
|
|
(a) => a.businessFunction === 'finance'
|
|
)
|
|
expect(financeActivities.length).toBeGreaterThanOrEqual(2)
|
|
})
|
|
|
|
it('end-to-end: departments selected in scope generate correct VVT activities', () => {
|
|
// Simulate a complete scope session with department selections
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [
|
|
// Block 2: data_art9 maps to data_health in VVT
|
|
ans('data_art9', true),
|
|
// Block 4: tech_third_country maps to transfer_cloud_us
|
|
ans('tech_third_country', true),
|
|
// Block 8: departments
|
|
ans('vvt_departments', ['personal', 'marketing', 'kundenservice']),
|
|
// Block 9: per-department data categories
|
|
ans('dk_dept_hr', ['NAME', 'HEALTH_DATA', 'RELIGIOUS_BELIEFS']),
|
|
ans('dk_dept_marketing', ['EMAIL', 'TRACKING_DATA', 'CONSENT_DATA']),
|
|
ans('dk_dept_support', ['NAME', 'TICKET_DATA', 'COMMUNICATION_DATA']),
|
|
]
|
|
|
|
// Transform to VVT answers
|
|
const vvtAnswers = exportToVVTAnswers(scopeAnswers)
|
|
|
|
// Verify Block 9 data categories are mapped correctly
|
|
expect(vvtAnswers.dept_hr_categories).toEqual(['NAME', 'HEALTH_DATA', 'RELIGIOUS_BELIEFS'])
|
|
expect(vvtAnswers.dept_marketing_categories).toEqual([
|
|
'EMAIL',
|
|
'TRACKING_DATA',
|
|
'CONSENT_DATA',
|
|
])
|
|
expect(vvtAnswers.dept_support_categories).toEqual([
|
|
'NAME',
|
|
'TICKET_DATA',
|
|
'COMMUNICATION_DATA',
|
|
])
|
|
|
|
// Verify the full pipeline using direct export
|
|
const profilingAnswers = scopeToProfilingAnswers(scopeAnswers)
|
|
expect(profilingAnswers.dept_hr_categories).toBeDefined()
|
|
expect(profilingAnswers.dept_marketing_categories).toBeDefined()
|
|
expect(profilingAnswers.dept_support_categories).toBeDefined()
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 6. DEPARTMENT_DATA_CATEGORIES Integrity
|
|
// =============================================================================
|
|
|
|
describe('DEPARTMENT_DATA_CATEGORIES consistency', () => {
|
|
it('all 12 departments are defined', () => {
|
|
const expected = [
|
|
'dept_hr',
|
|
'dept_recruiting',
|
|
'dept_finance',
|
|
'dept_sales',
|
|
'dept_marketing',
|
|
'dept_support',
|
|
'dept_it',
|
|
'dept_recht',
|
|
'dept_produktion',
|
|
'dept_logistik',
|
|
'dept_einkauf',
|
|
'dept_facility',
|
|
]
|
|
for (const dept of expected) {
|
|
expect(DEPARTMENT_DATA_CATEGORIES[dept]).toBeDefined()
|
|
expect(DEPARTMENT_DATA_CATEGORIES[dept].categories.length).toBeGreaterThan(0)
|
|
}
|
|
})
|
|
|
|
it('every department has a label and icon', () => {
|
|
for (const [, dept] of Object.entries(DEPARTMENT_DATA_CATEGORIES)) {
|
|
expect(dept.label).toBeTruthy()
|
|
expect(dept.icon).toBeTruthy()
|
|
}
|
|
})
|
|
|
|
it('every category has id and label', () => {
|
|
for (const [, dept] of Object.entries(DEPARTMENT_DATA_CATEGORIES)) {
|
|
for (const cat of dept.categories) {
|
|
expect(cat.id).toBeTruthy()
|
|
expect(cat.label).toBeTruthy()
|
|
expect(cat.info).toBeTruthy()
|
|
}
|
|
}
|
|
})
|
|
|
|
it('Art. 9 categories are correctly flagged', () => {
|
|
const art9Categories = [
|
|
{ dept: 'dept_hr', id: 'HEALTH_DATA' },
|
|
{ dept: 'dept_hr', id: 'RELIGIOUS_BELIEFS' },
|
|
{ dept: 'dept_recruiting', id: 'HEALTH_DATA' },
|
|
{ dept: 'dept_recht', id: 'CRIMINAL_DATA' },
|
|
{ dept: 'dept_produktion', id: 'HEALTH_DATA' },
|
|
{ dept: 'dept_facility', id: 'HEALTH_DATA' },
|
|
]
|
|
|
|
for (const { dept, id } of art9Categories) {
|
|
const cat = DEPARTMENT_DATA_CATEGORIES[dept].categories.find((c) => c.id === id)
|
|
expect(cat?.isArt9).toBe(true)
|
|
}
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 7. Block 9 ↔ VVT Mapping Integrity
|
|
// =============================================================================
|
|
|
|
describe('Block 9 Scope ↔ VVT question mapping', () => {
|
|
it('every Block 9 question has mapsToVVTQuestion', () => {
|
|
const block9 = SCOPE_QUESTION_BLOCKS.find((b) => b.id === 'datenkategorien_detail')
|
|
expect(block9).toBeDefined()
|
|
|
|
for (const q of block9!.questions) {
|
|
expect(q.mapsToVVTQuestion).toBeTruthy()
|
|
expect(q.mapsToVVTQuestion).toMatch(/^dept_\w+_categories$/)
|
|
}
|
|
})
|
|
|
|
it('Block 9 question options match DEPARTMENT_DATA_CATEGORIES', () => {
|
|
const block9 = SCOPE_QUESTION_BLOCKS.find((b) => b.id === 'datenkategorien_detail')
|
|
expect(block9).toBeDefined()
|
|
|
|
// dk_dept_hr should have same options as DEPARTMENT_DATA_CATEGORIES.dept_hr
|
|
const hrQuestion = block9!.questions.find((q) => q.id === 'dk_dept_hr')
|
|
expect(hrQuestion).toBeDefined()
|
|
|
|
const expectedIds = DEPARTMENT_DATA_CATEGORIES.dept_hr.categories.map((c) => c.id)
|
|
const actualIds = hrQuestion!.options!.map((o) => o.value)
|
|
expect(actualIds).toEqual(expectedIds)
|
|
})
|
|
|
|
it('SCOPE_PREFILLED_VVT_QUESTIONS lists all cross-module questions', () => {
|
|
expect(SCOPE_PREFILLED_VVT_QUESTIONS).toContain('org_industry')
|
|
expect(SCOPE_PREFILLED_VVT_QUESTIONS).toContain('dept_hr')
|
|
expect(SCOPE_PREFILLED_VVT_QUESTIONS).toContain('data_health')
|
|
expect(SCOPE_PREFILLED_VVT_QUESTIONS).toContain('transfer_cloud_us')
|
|
expect(SCOPE_PREFILLED_VVT_QUESTIONS.length).toBeGreaterThanOrEqual(15)
|
|
})
|
|
})
|
|
|
|
// =============================================================================
|
|
// 8. Edge Cases
|
|
// =============================================================================
|
|
|
|
describe('Edge cases', () => {
|
|
it('generateActivities with no answers still produces IT baselines', () => {
|
|
const result = generateActivities({})
|
|
expect(result.generatedActivities.length).toBe(4) // 4 IT baselines
|
|
expect(result.art30Abs5Exempt).toBe(true) // 0 employees, no special categories
|
|
})
|
|
|
|
it('same template triggered by multiple questions is only generated once', () => {
|
|
const result = generateActivities({
|
|
dept_sales: true, // triggers sales-kundenverwaltung
|
|
sys_crm: true, // also triggers sales-kundenverwaltung
|
|
})
|
|
|
|
const salesKunden = result.generatedActivities.filter((a) =>
|
|
a.name.toLowerCase().includes('kundenverwaltung')
|
|
)
|
|
// Should be deduplicated (Set-based triggeredIds)
|
|
expect(salesKunden.length).toBe(1)
|
|
})
|
|
|
|
it('empty department category selections produce valid but empty mappings', () => {
|
|
const scopeAnswers: ScopeProfilingAnswer[] = [ans('dk_dept_hr', [])]
|
|
const vvtAnswers = exportToVVTAnswers(scopeAnswers)
|
|
expect(vvtAnswers.dept_hr_categories).toEqual([])
|
|
})
|
|
})
|