Project: workspace-doctolib · Branch: HEAD · Mode: ` · **Model:**claude-sonnet-4-6`
Stats: 1 user messages, 35 tool calls, tools used: Bash, Read.
Conversation
Turn 1 — User
Analyze test coverage for PR https://github.com/doctolib/treatment-plan/pull/3129 (branch: fhir-2233-fix-toast-event-name in /Users/USER/workspace/doctolib/treatment-plan)
The PR fixes a pub/sub event name mismatch in three TPF toast helpers ('toasts:add' → 'toaster:add'). New unit test files were added: - packages/tpf-national-health-records-ui/src/business/toast.test.ts - packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.test.ts - packages/tpf-prescription-fulfillments/src/business/eprescription/toast.test.ts
Evaluate: Are the tests sufficient? Are edge cases covered? Are there gaps in coverage that could let a regression slip through?
Turn 1 — Assistant
I'll analyze the PR by examining the changed files and their tests systematically.
Tools used:
Bash:find /Users/USER/workspace/doctolib/worktrees/treatment-plan -maxdepth 2 -type d 2>/dev/null | head -20
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/worktrees/treatment-plan /Users/USER/workspace/doctolib/worktrees/treatment-plan/fhir-2223_fix-column-widths-truncation /Users/USER/workspace/doctolib/worktrees/treatment-plan/fhir-2168_add-result-count-to-nhr-header /Users/USER/workspace/doctolib/worktrees/treatment-plan/fhir-2171 /Users/USER/workspace/doctolib/worktrees/treatment-plan/fhir-2221_display-last-dmp-access-date /Users/USER/workspace/doctolib/worktrees/treatment-plan/fhir-2081 /Users/USER/workspace/doc …(truncated, 1283 more chars)
Tools used:
Bash:ls /Users/USER/workspace/doctolib/treatment-plan/
Tool results:
→ result (ok): AGENTS.md backend bin build catalog-info.yaml CLAUDE.md CODEOWNERS deployment dist doc docker docker-compose.yml Dockerfile eslint.config.js mise.toml mkdocs.yml module-federation.json mvnw mvnw.cmd node_modules nx.json openspec package-lock.json package.json packages pom.xml postcss.config.js prettier.config.js README.md release-please-config.json renovate.json5 service.datadog.yaml sonar-project.properties src tailwind.config.js target tests tpf.code-workspace tsconfig-base.json tsconfig-desig …(truncated, 144 more chars)
Tools used:
Bash:ls /Users/USER/workspace/doctolib/worktrees/treatment-plan/ | grep fhir-2233
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:git branch -a | grep fhir-2233
Tool results:
→ result (ok): * fhir-2233-fix-toast-event-name remotes/origin/fhir-2233-fix-toast-event-name
The branch is checked out in the main treatment-plan directory. Let me read all relevant files.
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.ts
Tool results:
→ result (ok): 1 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
2 import type { ReactNode } from 'react'
3
4 type ToastProps = {
5 message: ReactNode
6 title: string
7 uiStyle: 'info' | 'success' | 'error' | 'warning'
8 action?: { message: string; onClick: () => void }
9 }
10
11 export function addToast(props: ToastProps) {
12 GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
13 }
14
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.test.ts
Tool results:
→ result (ok): 1 import { afterEach, describe, expect, it, vi } from 'vitest'
2
3 import { addToast } from './toast'
4
5 vi.mock('@doctolib/upfront-core', () => ({
6 GLOBAL_PUBSUB_REGISTRY: {
7 dispatch: vi.fn(),
8 },
9 }))
10
11 describe('addToast', () => {
12 afterEach(() => {
13 vi.clearAllMocks()
14 })
15
16 it('dispatches toaster:add event with the given props', async () => {
17 const { GLOBAL_PUBSUB_REGISTRY } = await import('@doctolib/upfront-core')
18 const props = { title
…(truncated, 773 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts
Tool results:
→ result (ok): 1 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
2
3 type ToastProps = {
4 message: React.ReactNode
5 title: string
6 uiStyle: 'info' | 'success' | 'error' | 'warning'
7 action?: { message: string; onClick: () => void }
8 }
9
10 export function addToast(props: ToastProps) {
11 GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
12 }
13
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.test.ts
Tool results:
→ result (ok): 1 import { afterEach, describe, expect, it, vi } from 'vitest'
2
3 import { addToast } from './toast'
4
5 vi.mock('@doctolib/upfront-core', () => ({
6 GLOBAL_PUBSUB_REGISTRY: {
7 dispatch: vi.fn(),
8 },
9 }))
10
11 describe('addToast', () => {
12 afterEach(() => {
13 vi.clearAllMocks()
14 })
15
16 it('dispatches toaster:add event with the given props', async () => {
17 const { GLOBAL_PUBSUB_REGISTRY } = await import('@doctolib/upfront-core')
18 const props = { title
…(truncated, 254 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/toast.ts
Tool results:
→ result (ok): 1 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
2
3 export type ToastProps = {
4 message: React.ReactNode
5 title: string
6 uiStyle: 'info' | 'success' | 'error' | 'warning'
7 action?: { message: string; onClick: () => void }
8 }
9
10 export const addToast = (props: ToastProps) => {
11 GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
12 }
13
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/toast.test.ts
Tool results:
→ result (ok): 1 import { afterEach, describe, expect, it, vi } from 'vitest'
2
3 import { addToast } from './toast'
4
5 vi.mock('@doctolib/upfront-core', () => ({
6 GLOBAL_PUBSUB_REGISTRY: {
7 dispatch: vi.fn(),
8 },
9 }))
10
11 describe('addToast', () => {
12 afterEach(() => {
13 vi.clearAllMocks()
14 })
15
16 it('dispatches toaster:add event with the given props', async () => {
17 const { GLOBAL_PUBSUB_REGISTRY } = await import('@doctolib/upfront-core')
18 const props = { title
…(truncated, 254 more chars)
Now let me look at the git diff for the full context of the PR, and also investigate how callers of addToast use it to understand the broader impact.
Tools used:
Bash:git log --oneline origin/main..HEAD 2>/dev/null | head -20
Tool results:
→ result (ok): 9e0bfd841 fix(fhir-2233): align toast pub/sub event name with monolith toaster
Tools used:
Bash:git diff origin/main..HEAD --stat 2>/dev/null
Tool results:
→ result (ok): .../src/business/toast.test.ts | 44 ++++++++++++++++++++++ .../src/business/toast.ts | 2 +- .../dmp-consultation-consent-view/toast.test.ts | 27 +++++++++++++ .../dmp-consultation-consent-view/toast.ts | 8 +--- .../src/business/eprescription/toast.test.ts | 27 +++++++++++++ .../src/business/eprescription/toast.ts | 2 +- 6 files changed, 101 insertions(+), 9 deletions(-)
Tools used:
Bash:git diff origin/main..HEAD 2>/dev/null
Tool results:
→ result (ok): diff --git a/packages/tpf-national-health-records-ui/src/business/toast.test.ts b/packages/tpf-national-health-records-ui/src/business/toast.test.ts new file mode 100644 index 000000000..0da6c1b6e --- /dev/null +++ b/packages/tpf-national-health-records-ui/src/business/toast.test.ts @@ -0,0 +1,44 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +import { addToast } from './toast' + +vi.mock('@doctolib/upfront-core', () => ({ + GLOBAL_PUBSUB_REGISTRY: { + dispatch: vi.fn(),
…(truncated, 5159 more chars)
Tools used:
Bash:grep -r "addToast" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.ts" --include="*.tsx" -l
Tools used:
Bash:grep -r "toasts:add\|toaster:add" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.ts" --include="*.tsx" -n
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-view.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-view.test.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp- …(truncated, 3139 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx:31: const unsubscribe = GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', (event) => { /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts:11: GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props }) /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.test.ts:1 …(truncated, 1637 more chars)
Tools used:
Bash:grep -n "toasts:add" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx
Tool results:
→ result (ok): 31: const unsubscribe = GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', (event) => {
Tools used:
Bash:grep -r "toasts:add" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.ts" --include="*.tsx" -rn
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx:31: const unsubscribe = GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', (event) => {
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx
Tool results:
→ result (ok): 1 import '@doctolib/design-system/design-system.scss'
2 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
3 import { action } from '@storybook/addon-actions'
4 import type { Preview } from '@storybook/react'
5 import { useEffect } from 'react'
6
7 import { DmpPatientFileContextProvider } from '../src/hooks'
8 import { getInstance } from '../src/i18n'
9
10 const i18n = getInstance()
11
12 let isI18nInitialized = false
13
14 const i18nInitLoader = async () => {
15 if (!isI18nIn
…(truncated, 1221 more chars)
Tools used:
Bash:grep -r "toasts:add\|toaster:add" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.ts" --include="*.tsx" -rn | grep -v "\.test\." | grep -v "/dist/"
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx:31: const unsubscribe = GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', (event) => { /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts:11: GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props }) /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.ts:12: GLOBAL_PUBSUB …(truncated, 258 more chars)
Tools used:
Bash:grep -rn "addToast" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src --include="*.ts" --include="*.tsx"
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.ts:11:export function addToast(props: ToastProps) { /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.test.ts:3:import { addToast } from './toast' /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.test.ts:11:describe('addToast', () => { /Users/USER/workspace/doctolib/treatment-plan/pa …(truncated, 2747 more chars)
Tools used:
Bash:grep -rn "addToast" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src --include="*.ts" --include="*.tsx"
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/usePrintMedicationPrescription.ts:4:import { addToast } from '../eprescription/toast' /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/usePrintMedicationPrescription.ts:24: addToast({ /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/toast.ts:10:export const addToast = (pro …(truncated, 3011 more chars)
Tools used:
Bash:grep -rn "addToast" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src --include="*.ts" --include="*.tsx"
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-view.test.tsx:14:import { addToast } from './toast' /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-view.test.tsx:52: addToast: vi.fn(), /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-vi …(truncated, 2269 more chars)
Tools used:
Bash:grep -rn "addToast" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src --include="*.ts" --include="*.tsx"
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/exported-types.ts:90: addToast?: MonolithAddToastFn /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/components/RichTextsCreateFormModule.tsx:37: addToast?: MonolithAddToastFn /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/components/RichTextsCreateFormModule.tsx:63: addToast, /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src …(truncated, 2314 more chars)
Tools used:
Bash:grep -rn "MonolithAddToastFn\|toasts:add\|toaster:add" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src --include="*.ts" --include="*.tsx" | grep -v "\.test\."
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/exported-types.ts:4: MonolithAddToastFn, /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/exported-types.ts:90: addToast?: MonolithAddToastFn /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/components/RichTextsCreateFormModule.tsx:5: type MonolithAddToastFn, /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src/components/RichText …(truncated, 943 more chars)
Tools used:
Bash:grep -rn "MonolithAddToastFn" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.ts" --include="*.tsx" -l
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/node_modules/@doctolib/tpf-commons/dist/types/monolith/index.d.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/node_modules/@doctolib/tpf-commons/src/types/monolith/index.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-commons/dist/types/monolith/index.d.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-commons/src/types/monolith/index.ts /Users/USER/workspace/doctolib/treatment-plan/ …(truncated, 1102 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-commons/src/types/monolith/index.ts
Tool results:
→ result (ok): 1 import type { Account } from './account' 2 import type { Consultation } from './consultation' 3 import type { UnifiedDocument } from './document' 4 import type { HttpClient } from './httpClient' 5 import type { MedicalFolder } from './medical-folder' 6 import type { SendMonitoringEvent } from './monitoring' 7 import { InsuranceSector } from './patient' 8 import type { Patient } from './patient' 9 import type { Practice } from './practice' 10 import type { BaseQuarterlyBillingCase } from './qua …(truncated, 1142 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/usePrintMedicationPrescription.ts
Tool results:
→ result (ok): 1 import { i18n } from '@doctolib/tpf-commons/i18n'
2
3 import type { HttpClient } from '../eprescription/injectionTypes'
4 import { addToast } from '../eprescription/toast'
5 import type { MedicationPdfRequest } from './types'
6 import { useMedicationPdfGeneration } from './useMedicationPdfGeneration'
7
8 const BLOB_URL_REVOKE_DELAY_MS = 60_000
9
10 export type UsePrintMedicationPrescriptionDeps = {
11 httpClient: HttpClient
12 }
13
14 export const usePrintMedicationPrescription = ({ http
…(truncated, 790 more chars)
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-commons/src/types/monolith -name "toast.ts" | xargs cat
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src -name "*.test.ts" -o -name "*.test.tsx" | grep -i "print\|usePrint" | head -10
Tool results:
→ result (ok): import type { MouseEventHandler, ReactNode } from 'react'
export type ToastUiStyle = 'info' | 'warning' | 'success' | 'error' | 'loading'
type ToastAction = { message: string onClick: MouseEventHandler dataWalkme?: string trackingId?: string }
type ToastMessageProps = { duration: number onHide?: (id?: number) => void }
export type AddToastProps = { uiStyle?: ToastUiStyle title?: string message?: ReactNode | ((messageProps: ToastMessageProps) => ReactNode) action?: ToastAc …(truncated, 303 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/useMedicationPdfGeneration.test.ts
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print -name "*.test.ts" -o -name "*.test.tsx" | head -5
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/useMedicationPdfGeneration.test.ts
Tools used:
Bash:ls /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/
Tool results:
→ result (ok): types.ts useMedicationPdfGeneration.test.ts useMedicationPdfGeneration.ts usePrintMedicationPrescription.ts
Tools used:
Bash:grep -rn "toasts:add\|toaster:add" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-rich-texts-form/src --include="*.ts" --include="*.tsx" -n
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -rn "GLOBAL_PUBSUB_REGISTRY" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.ts" --include="*.tsx" | grep -v "\.test\." | grep -v "/dist/" | grep -v "/node_modules/"
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx:2:import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core' /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx:31: const unsubscribe = GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', (event) => { /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts:1:import { GLOBAL_PUBSUB_REGISTRY } from '@doctol …(truncated, 8834 more chars)
Now I have a complete picture. Let me verify one more detail — whether there are any other storybook preview files that subscribe to either event name:
Tools used:
Bash:grep -rn "toasts:add\|toaster:add" /Users/USER/workspace/doctolib/treatment-plan/packages --include="*.tsx" --include="*.ts" | grep -i "storybook\|preview\|\.stories\."
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages -name "preview.tsx" | xargs grep -l "toasts:add\|toaster:add" 2>/dev/null
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx:31: const unsubscribe = GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', (event) => {
I now have all the information needed for a thorough analysis. Here is the complete evaluation:
Summary
The three new test files cover the core contract of the bug fix — each verifies that addToast dispatches toaster:add (the corrected event name) rather than toasts:add (the broken one). The tests are correctly structured, use proper cleanup with afterEach(vi.clearAllMocks()), follow the project's Vitest conventions, and would catch a regression to the old event name. For such a narrow, surgical bug fix, the coverage is mostly adequate.
However, there are several meaningful gaps identified below.
Critical Gaps
Gap 1: Storybook preview in tpf-nhr-fr still subscribes to the old toasts:add event (criticality: 8)
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx, line 31, still calls GLOBAL_PUBSUB_REGISTRY.subscribe('toasts:add', ...) while the dmp-consultation-consent-view/toast.ts in the same package now dispatches toaster:add. This is a direct functional mismatch left behind by the PR. Toasts triggered from the DMP consent view will be silently dropped in Storybook, making developer verification unreliable — and, more importantly, it is evidence that the audit for toasts:add usages was incomplete. The Storybook comment (// the monolith listens for 'toasts:add' events) was removed from the source file but the subscriber was not updated.
This file is not a test file, so no test would catch it — but the PR should have fixed it. A regression test verifying that the subscriber and dispatcher use the same event name would prevent this class of mismatch in the future.
Gap 2: usePrintMedicationPrescription is an untested caller of the fixed addToast (criticality: 7)
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/print/usePrintMedicationPrescription.ts calls addToast in its catch block (line 24) but has no test file of its own. Only useMedicationPdfGeneration.test.ts exists in that directory, which tests the PDF generation side only. A failure in PDF printing silently eats the error without showing any toast to the user if that path breaks. This is not introduced by this PR, but the PR touches the very module this code depends on, making it a relevant gap to flag.
Gap 3: Two of the three test files only cover one uiStyle variant ('error'); they do not cover 'success', 'info', or 'warning' (criticality: 5)
The tpf-nhr-fr and tpf-prescription-fulfillments toast tests each have a single test case using uiStyle: 'error'. The tpf-national-health-records-ui test adds a second case covering uiStyle: 'info' with an optional action, which is better. Since the implementation passes props through verbatim, none of these would realistically fail differently per style — but the opt-in action field is only exercised in one of three packages. Given the fix is specifically about the event contract, a test with action: undefined vs action: { message, onClick } demonstrates the full payload shape is preserved in all cases. This is a minor gap.
Important Improvements
Improvement 1: The tpf-nhr-fr and tpf-prescription-fulfillments tests should each cover the optional action prop (criticality: 5)
The tpf-national-health-records-ui test correctly adds a second case that verifies action is passed through intact in the toaster:add payload. The other two packages' tests omit this. If someone were to accidentally strip the action field from the dispatch payload in those two packages, no test would catch it. Adding one test case exercising action: { message: 'View', onClick: vi.fn() } to each of those two files provides parity and prevents this class of regression.
// Suggested addition for tpf-nhr-fr/toast.test.ts and tpf-prescription-fulfillments/toast.test.ts:
it('dispatches toaster:add event with optional action prop', async () => {
const { GLOBAL_PUBSUB_REGISTRY } = await import('@doctolib/upfront-core')
const props = {
title: 'Info',
message: 'See details',
uiStyle: 'info' as const,
action: { message: 'Retry', onClick: vi.fn() },
}
addToast(props)
expect(GLOBAL_PUBSUB_REGISTRY.dispatch).toHaveBeenCalledWith({
type: 'toaster:add',
payload: props,
})
})
Improvement 2: The tpf-nhr-fr toast test does not assert that dispatch is called exactly once (criticality: 4)
None of the tests use toHaveBeenCalledTimes(1). If someone changes addToast to dispatch multiple events (perhaps accidentally composing two calls), the current assertions would still pass because toHaveBeenCalledWith checks that the mock was called with those args at least once. Adding toHaveBeenCalledTimes(1) as an additional assertion would catch over-triggering.
Test Quality Issues
Issue 1: Dynamic import() inside test bodies is unnecessary and inconsistent with the mock already established by vi.mock at module scope
All three test files use vi.mock(...) at the top (hoisted to module scope by Vitest), then re-import the mocked module with await import(...) inside each it block to access the mock. This is a known pattern for accessing Vitest module mocks, but it creates an async test where the function under test (addToast) is synchronous. The mock reference could instead be captured once in a beforeEach or at the describe level using vi.mocked(), which is simpler and avoids async in test functions that don't otherwise need it. This does not affect correctness but adds unnecessary complexity.
Issue 2: tpf-nhr-fr dmp-consultation-consent-view.test.tsx mocks addToast at the module level, so the behavior-level coverage of the dispatch contract depends entirely on the new toast.test.ts — which is correct separation, but only works if both test files are run
The component test correctly mocks addToast and asserts it's called with the right uiStyle. This design is sound: the component test verifies the component calls addToast; the unit test verifies addToast dispatches toaster:add. The layering is appropriate and follows the project's testing conventions. No issue here — flagging it positively (see Positive Observations).
Positive Observations
- All three test files correctly pin the exact event
typestring'toaster:add'in the assertion, meaning any future typo or drift back to'toasts:add'would immediately fail the test. This is the most important property for a bug-fix regression test. afterEach(vi.clearAllMocks())is consistently present in all files, ensuring test isolation.- The
tpf-national-health-records-uitest is the most thorough of the three — it covers two prop shapes (with and withoutaction) and follows the project testing conventions closely with DAMP test names. - The layering between component tests (which mock
addToast) and unit tests (which testaddToastdirectly) is correct and consistent with the project's approach. Callers likeNhrDocumentListModal.test.tsxanddmp-consultation-consent-view.test.tsxcontinue to test the calling behavior at the right layer. - There are no snapshot tests, no CSS selectors, and no arbitrary waits — the tests follow the project's prohibited patterns correctly.
- The fix correctly identifies and updates all three source files that contained the wrong event name. No instance of
toasts:addremains in the productiondispatchpaths (only the storybook subscriber noted above, which is a missed update in this PR, not a test gap).
Action Required (Not a Test Gap — a Code Bug in the PR)
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx at line 31 still subscribes to 'toasts:add'. The PR fixed the dispatcher but missed the subscriber. Toasts from the DMP consent view will be invisible in Storybook as a result. This should be updated in this PR to 'toaster:add' to mat
…(truncated, 83 more chars)