Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions components/compliance/AccessControlEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function AccessControlEditor({ disabled, guidance, initialData }: { disab
onValueChange={(v) => draft && setDraft({ ...draft, model: v as "rbac" | "abac" | "hybrid" })}
disabled={fieldsDisabled}
>
<SelectTrigger className="max-w-xs"><SelectValue /></SelectTrigger>
<SelectTrigger className="max-w-xs" data-testid="access-model-select"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="rbac">{tc("rbac")}</SelectItem>
<SelectItem value="abac">{tc("abac")}</SelectItem>
Expand All @@ -60,6 +60,7 @@ export function AccessControlEditor({ disabled, guidance, initialData }: { disab
<div className="space-y-1.5">
<label className="text-xs text-muted-foreground">{tc("standardReview")}</label>
<Input
data-testid="access-review-standard"
value={display.reviewFrequency.standard}
onChange={(e) =>
draft && setDraft({
Expand All @@ -74,6 +75,7 @@ export function AccessControlEditor({ disabled, guidance, initialData }: { disab
<div className="space-y-1.5">
<label className="text-xs text-muted-foreground">{tc("privilegedReview")}</label>
<Input
data-testid="access-review-privileged"
value={display.reviewFrequency.privileged}
onChange={(e) =>
draft && setDraft({
Expand Down Expand Up @@ -111,7 +113,7 @@ export function AccessControlEditor({ disabled, guidance, initialData }: { disab
onValueChange={(v) => draft && setDraft({ ...draft, sharedAccountPolicy: v as "prohibited" | "documented_exceptions" })}
disabled={fieldsDisabled}
>
<SelectTrigger className="max-w-xs"><SelectValue /></SelectTrigger>
<SelectTrigger className="max-w-xs" data-testid="shared-account-select"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="prohibited">{tc("sharedProhibited")}</SelectItem>
<SelectItem value="documented_exceptions">{tc("sharedDocumentedExceptions")}</SelectItem>
Expand Down
1 change: 1 addition & 0 deletions components/compliance/CryptoPolicyEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ export function CryptoPolicyEditor({ disabled, guidance, initialData }: { disabl
<label className="text-sm font-medium">{tc("keyRotation")}</label>
<Input
type="number"
data-testid="crypto-key-rotation"
min={1}
max={10}
value={display.keyRotationFrequencyYears}
Expand Down
2 changes: 2 additions & 0 deletions components/compliance/PatchPolicyEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function PatchPolicyEditor({ disabled, guidance, initialData }: { disable
<td className="px-3 py-2">
<Input
type="number"
data-testid={`patch-sla-${sev}`}
min={1}
max={8760}
value={display.patchSlaHours[sev]}
Expand All @@ -73,6 +74,7 @@ export function PatchPolicyEditor({ disabled, guidance, initialData }: { disable
<label className="text-sm font-medium">{tc("reviewCycle")}</label>
<Input
type="number"
data-testid="patch-review-cycle"
min={1}
max={10}
value={display.reviewCycleYears}
Expand Down
6 changes: 3 additions & 3 deletions components/compliance/PolicyEditorShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ export function PolicyEditorShell({
{!disabled &&
(isEditing ? (
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={onCancel}>
<Button variant="outline" size="sm" data-testid="policy-editor-cancel" onClick={onCancel}>
{t("cancel")}
</Button>
<Button size="sm" onClick={onSave} disabled={isPending}>
<Button size="sm" data-testid="policy-editor-save" onClick={onSave} disabled={isPending}>
{isPending ? (
<Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
) : (
Expand All @@ -71,7 +71,7 @@ export function PolicyEditorShell({
</Button>
</div>
) : (
<Button variant="outline" size="sm" onClick={onEdit}>
<Button variant="outline" size="sm" data-testid="policy-editor-edit" onClick={onEdit}>
<Pencil className="mr-1.5 h-3.5 w-3.5" />
{t("edit")}
</Button>
Expand Down
2 changes: 2 additions & 0 deletions components/compliance/ProcurementEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function ProcurementEditor({ disabled, guidance, initialData }: { disable
<SectionGuidance guidance={guidance} fieldKey="threshold" />
<Input
type="number"
data-testid="procurement-threshold"
min={0}
value={display.thresholdEur}
onChange={(e) => draft && setDraft({ ...draft, thresholdEur: Number(e.target.value) || 0 })}
Expand All @@ -63,6 +64,7 @@ export function ProcurementEditor({ disabled, guidance, initialData }: { disable
{CLAUSE_KEYS.map((key) => (
<div key={key} className="flex items-center gap-2">
<Checkbox
data-testid={`procurement-clause-${key}`}
checked={display.requiredClauses[key]}
onCheckedChange={(v) =>
draft && setDraft({
Expand Down
6 changes: 4 additions & 2 deletions components/compliance/RiskMethodologyEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ export function RiskMethodologyEditor({
</Button>
<Button
size="sm"
data-testid="methodology-save"
onClick={handleSave}
disabled={updateMut.isPending}
>
Expand All @@ -327,7 +328,7 @@ export function RiskMethodologyEditor({
</Button>
</div>
) : (
<Button variant="outline" size="sm" onClick={startEditing}>
<Button variant="outline" size="sm" data-testid="methodology-edit" onClick={startEditing}>
<Pencil className="mr-1.5 h-3.5 w-3.5" />
{t("edit")}
</Button>
Expand All @@ -340,6 +341,7 @@ export function RiskMethodologyEditor({
<label className="text-sm font-medium">{t("methodologyName")}</label>
<SectionGuidance guidance={guidance} fieldKey="methodologyName" />
<Input
data-testid="methodology-name"
value={displayName}
onChange={(e) => setName(e.target.value)}
disabled={fieldsDisabled}
Expand Down Expand Up @@ -390,7 +392,7 @@ export function RiskMethodologyEditor({
onValueChange={(v) => setAcceptanceThreshold(Number(v))}
disabled={fieldsDisabled}
>
<SelectTrigger className="w-20">
<SelectTrigger className="w-20" data-testid="methodology-threshold">
<SelectValue />
</SelectTrigger>
<SelectContent>
Expand Down
5 changes: 3 additions & 2 deletions components/compliance/SecureDevEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function SecureDevEditor({ disabled, guidance, initialData }: { disabled?
}
disabled={fieldsDisabled}
>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectTrigger data-testid="sdlc-framework-select"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="owasp_samm">{tc("owaspSamm")}</SelectItem>
<SelectItem value="bsimm">{tc("bsimm")}</SelectItem>
Expand All @@ -70,7 +70,7 @@ export function SecureDevEditor({ disabled, guidance, initialData }: { disabled?
}
disabled={fieldsDisabled}
>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectTrigger data-testid="hardening-baseline-select"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="cis">{tc("cis")}</SelectItem>
<SelectItem value="bsi">{tc("bsi")}</SelectItem>
Expand Down Expand Up @@ -108,6 +108,7 @@ export function SecureDevEditor({ disabled, guidance, initialData }: { disabled?
{/* Environment Segregation — CIR 6(2) */}
<div className="flex items-center gap-2">
<Checkbox
data-testid="environment-segregation"
checked={display.environmentSegregation}
onCheckedChange={(v) =>
draft && setDraft({ ...draft, environmentSegregation: !!v })
Expand Down
4 changes: 3 additions & 1 deletion components/risks/AssetRiskRegister.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export function AssetRiskRegister({ initialData, disabled }: AssetRiskRegisterPr
<Button
variant="ghost"
size="sm"
data-testid="risk-register-add"
onClick={() => { setAddingToAsset(asset.id); setExpandedAssets(prev => new Set(prev).add(asset.id)); }}
>
<Plus className="h-3.5 w-3.5 mr-1" />
Expand Down Expand Up @@ -402,7 +403,7 @@ function InlineRiskForm({
{/* Title (editable after threat selection or manual) */}
<div className="space-y-1.5">
<Label className="text-xs">{t("fields.title")}</Label>
<Input value={title} onChange={e => setTitle(e.target.value)} disabled={disabled} />
<Input data-testid="risk-form-title" value={title} onChange={e => setTitle(e.target.value)} disabled={disabled} />
</div>

<div className="space-y-1.5">
Expand Down Expand Up @@ -446,6 +447,7 @@ function InlineRiskForm({
<Button variant="ghost" size="sm" onClick={onCancel}>{t("cancel")}</Button>
<Button
size="sm"
data-testid="risk-form-submit"
onClick={handleSubmit}
disabled={!title || likelihood == null || impact == null || isPending || disabled}
>
Expand Down
1 change: 1 addition & 0 deletions components/risks/RiskMatrixPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function RiskMatrixPicker({
<td key={imp.value} className="px-1 py-1">
<button
type="button"
data-testid={`matrix-cell-${lik.value}-${imp.value}`}
disabled={disabled}
onClick={() => onChange(lik.value, imp.value)}
className={cn(
Expand Down
9 changes: 5 additions & 4 deletions components/risks/RiskTreatmentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export function RiskTreatmentView({ initialData, disabled }: RiskTreatmentViewPr
disabled={disabled}
/>
) : !disabled && (
<Button variant="ghost" size="sm" className="ml-4" onClick={() => setAddingTreatment(risk.id)}>
<Button variant="ghost" size="sm" className="ml-4" data-testid="treatment-add" onClick={() => setAddingTreatment(risk.id)}>
<Plus className="h-3.5 w-3.5 mr-1" />
{t("addMeasure")}
</Button>
Expand All @@ -213,7 +213,7 @@ export function RiskTreatmentView({ initialData, disabled }: RiskTreatmentViewPr
<Button variant="ghost" size="sm" onClick={() => setSettingResidual(null)}>{t("cancel")}</Button>
</div>
) : (
<Button variant="outline" size="sm" onClick={() => setSettingResidual(risk.id)} disabled={disabled}>
<Button variant="outline" size="sm" data-testid="residual-open" onClick={() => setSettingResidual(risk.id)} disabled={disabled}>
{hasResidual ? t("updateResidual") : t("setResidual")}
</Button>
)}
Expand All @@ -223,6 +223,7 @@ export function RiskTreatmentView({ initialData, disabled }: RiskTreatmentViewPr
{residualBelow && !risk.acceptedAt && !disabled && (
<Button
size="sm"
data-testid="risk-accept"
onClick={() => updateRiskMut.mutate({ id: risk.id, acceptedAt: new Date() })}
disabled={updateRiskMut.isPending}
>
Expand Down Expand Up @@ -292,15 +293,15 @@ function AddTreatmentForm({
<div className="ml-4 p-3 border rounded-lg bg-muted/30 space-y-3">
<div className="space-y-1.5">
<Label className="text-xs">{t("measureAction")}</Label>
<Input value={action} onChange={e => setAction(e.target.value)} disabled={disabled} placeholder={t("measureActionPlaceholder")} />
<Input data-testid="treatment-action" value={action} onChange={e => setAction(e.target.value)} disabled={disabled} placeholder={t("measureActionPlaceholder")} />
</div>
<div className="space-y-1.5">
<Label className="text-xs">{t("fields.description")}</Label>
<Textarea value={description} onChange={e => setDescription(e.target.value)} rows={2} disabled={disabled} />
</div>
<div className="flex justify-end gap-2">
<Button variant="ghost" size="sm" onClick={onCancel}>{t("cancel")}</Button>
<Button size="sm" onClick={() => onSave({ riskId, action, description })} disabled={!action || isPending || disabled}>
<Button size="sm" data-testid="treatment-submit" onClick={() => onSave({ riskId, action, description })} disabled={!action || isPending || disabled}>
{isPending ? <Loader2 className="h-4 w-4 animate-spin" /> : t("addMeasure")}
</Button>
</div>
Expand Down
4 changes: 3 additions & 1 deletion components/suppliers/SupplierRiskRegister.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function SupplierRiskRegister({ initialData, disabled }: SupplierRiskRegi
<Button
variant="ghost"
size="sm"
data-testid="risk-register-add"
onClick={() => { setAddingToSupplier(sup.id); setExpandedSuppliers(prev => new Set(prev).add(sup.id)); }}
>
<Plus className="h-3.5 w-3.5 mr-1" />
Expand Down Expand Up @@ -399,7 +400,7 @@ function InlineRiskForm({
{/* Title */}
<div className="space-y-1.5">
<Label className="text-xs">{t("fields.title")}</Label>
<Input value={title} onChange={e => setTitle(e.target.value)} disabled={disabled} />
<Input data-testid="risk-form-title" value={title} onChange={e => setTitle(e.target.value)} disabled={disabled} />
</div>

<div className="space-y-1.5">
Expand Down Expand Up @@ -443,6 +444,7 @@ function InlineRiskForm({
<Button variant="ghost" size="sm" onClick={onCancel}>{t("cancel")}</Button>
<Button
size="sm"
data-testid="risk-form-submit"
onClick={handleSubmit}
disabled={!title || likelihood == null || impact == null || isPending || disabled}
>
Expand Down
9 changes: 7 additions & 2 deletions e2e/coverage-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ const MODULES_TESTED = new Set([
"internal_audit",
"management_review",
]);
const EDITORS_TESTED = new Set<string>(); // none yet
// l2/editors.spec.ts drives every custom editor, so the set IS the custom-
// editor registry — derive it rather than restate the nine codes a third time.
// NOTE: "tested" here means the editor's page was driven and persisted, not
// that every structured sub-table (algorithm/criteria rows) was exercised;
// those inner surfaces remain a partial gap.
const EDITORS_TESTED = UI_CUSTOM_EDITORS;

const rows: Row[] = nis2Categories
const rows: Row[] = [...nis2Categories]
.sort((a, b) => a.sortOrder - b.sortOrder)
.flatMap((cat) =>
getNis2RequirementsForCategory(cat.slug).map((r) => ({
Expand Down
Loading