{"_id":"@c-a-f/testing","_rev":"2-6eb86d87c2c62028a2cdcdc53cfda38f","name":"@c-a-f/testing","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.1":{"name":"@c-a-f/testing","version":"1.0.1","keywords":["clean-architecture-frontend","clean-architecture","caf","testing","test-utils","mocks","test-helpers"],"author":{"name":"ali aslani","email":"aliaslani.mm@gmail.com"},"license":"MIT","_id":"@c-a-f/testing@1.0.1","maintainers":[{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"}],"homepage":"https://github.com/ialiaslani/caf#readme","bugs":{"url":"https://github.com/ialiaslani/caf/issues"},"dist":{"shasum":"8ffab814105d81d203071b674cf9c40d1315c403","tarball":"https://registry.npmjs.org/@c-a-f/testing/-/testing-1.0.1.tgz","fileCount":70,"integrity":"sha512-HU1Fp4UDK1LlZGdZQtsrabQA+kI0e3jg28mtZh084rVsvjiPkgvQkuWDpe3cNyvsvDC4A28perx6GyhvvOFgBQ==","signatures":[{"sig":"MEYCIQDjfW+gTcJ5voQLMcUO6dHgeECg/7ZwPglkNKjtVYnCYgIhAI8epWJNaV5S5ZhW4nVsIMZwsvf7D+kIirS9ZqvNtZWm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":116006},"main":"./.build/index.js","type":"module","types":"./.build/index.d.ts","module":"./.build/index.js","exports":{".":{"node":"./.build/index.js","types":"./.build/index.d.ts","import":"./.build/index.js","default":"./.build/index.js"},"./vue":{"node":"./.build/src/vue/index.js","types":"./.build/src/vue/index.d.ts","import":"./.build/src/vue/index.js","default":"./.build/src/vue/index.js"},"./core":{"node":"./.build/src/core/index.js","types":"./.build/src/core/index.d.ts","import":"./.build/src/core/index.js","default":"./.build/src/core/index.js"},"./i18n":{"node":"./.build/src/i18n/index.js","types":"./.build/src/i18n/index.d.ts","import":"./.build/src/i18n/index.js","default":"./.build/src/i18n/index.js"},"./react":{"node":"./.build/src/react/index.js","types":"./.build/src/react/index.d.ts","import":"./.build/src/react/index.js","default":"./.build/src/react/index.js"},"./angular":{"node":"./.build/src/angular/index.js","types":"./.build/src/angular/index.d.ts","import":"./.build/src/angular/index.js","default":"./.build/src/angular/index.js"},"./workflow":{"node":"./.build/src/workflow/index.js","types":"./.build/src/workflow/index.d.ts","import":"./.build/src/workflow/index.js","default":"./.build/src/workflow/index.js"},"./permission":{"node":"./.build/src/permission/index.js","types":"./.build/src/permission/index.d.ts","import":"./.build/src/permission/index.js","default":"./.build/src/permission/index.js"},"./validation":{"node":"./.build/src/validation/index.js","types":"./.build/src/validation/index.d.ts","import":"./.build/src/validation/index.js","default":"./.build/src/validation/index.js"}},"gitHead":"80878f64d4fb78843f3e607c578304a4711f691c","scripts":{"test":"vitest run","build":"tsc --build","start":"tsc --watch","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"},"repository":{"url":"git+https://github.com/ialiaslani/caf.git","type":"git"},"_npmVersion":"10.9.2","description":"Testing utilities and helpers for CAF applications. Provides mocks, test helpers, and utilities for testing UseCase, Ploc, Pulse, Workflow, Permission, I18n, and Validation.","directories":{},"_nodeVersion":"22.13.0","dependencies":{"@c-a-f/core":"1.0.3","@c-a-f/i18n":"1.0.0","@c-a-f/workflow":"1.0.1","@c-a-f/permission":"1.0.0","@c-a-f/validation":"1.0.3","@c-a-f/infrastructure-vue":"^1.0.0","@c-a-f/infrastructure-react":"^1.0.4","@c-a-f/infrastructure-angular":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","vitest":"^2.1.0","happy-dom":"^15.11.7","@testing-library/react":"^16.0.0"},"peerDependencies":{"vue":">=3.0.0","react":">=16.8.0","@angular/core":">=18.0.0","@vue/test-utils":">=2.0.0","@testing-library/react":">=14.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true},"@angular/core":{"optional":true},"@vue/test-utils":{"optional":true},"@testing-library/react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/testing_1.0.1_1771672169860_0.822093919557692","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@c-a-f/testing","type":"module","version":"1.0.2","description":"Testing utilities and helpers for CAF applications. Provides mocks, test helpers, and utilities for testing UseCase, Ploc, Pulse, Workflow, Permission, I18n, and Validation.","keywords":["clean-architecture-frontend","clean-architecture","caf","testing","test-utils","mocks","test-helpers"],"author":{"name":"ali aslani","email":"aliaslani.mm@gmail.com"},"license":"MIT","homepage":"https://docs-caf.vercel.app/docs/packages/testing","repository":{"type":"git","url":"git+https://github.com/ialiaslani/caf.git"},"main":"./.build/index.js","module":"./.build/index.js","types":"./.build/index.d.ts","exports":{".":{"types":"./.build/index.d.ts","import":"./.build/index.js","node":"./.build/index.js","default":"./.build/index.js"},"./core":{"types":"./.build/src/core/index.d.ts","import":"./.build/src/core/index.js","node":"./.build/src/core/index.js","default":"./.build/src/core/index.js"},"./workflow":{"types":"./.build/src/workflow/index.d.ts","import":"./.build/src/workflow/index.js","node":"./.build/src/workflow/index.js","default":"./.build/src/workflow/index.js"},"./permission":{"types":"./.build/src/permission/index.d.ts","import":"./.build/src/permission/index.js","node":"./.build/src/permission/index.js","default":"./.build/src/permission/index.js"},"./i18n":{"types":"./.build/src/i18n/index.d.ts","import":"./.build/src/i18n/index.js","node":"./.build/src/i18n/index.js","default":"./.build/src/i18n/index.js"},"./validation":{"types":"./.build/src/validation/index.d.ts","import":"./.build/src/validation/index.js","node":"./.build/src/validation/index.js","default":"./.build/src/validation/index.js"},"./react":{"types":"./.build/src/react/index.d.ts","import":"./.build/src/react/index.js","node":"./.build/src/react/index.js","default":"./.build/src/react/index.js"},"./vue":{"types":"./.build/src/vue/index.d.ts","import":"./.build/src/vue/index.js","node":"./.build/src/vue/index.js","default":"./.build/src/vue/index.js"},"./angular":{"types":"./.build/src/angular/index.d.ts","import":"./.build/src/angular/index.js","node":"./.build/src/angular/index.js","default":"./.build/src/angular/index.js"}},"scripts":{"build":"tsc --build","start":"tsc --watch","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run build"},"dependencies":{"@c-a-f/core":"1.0.3","@c-a-f/infrastructure-angular":"^1.0.1","@c-a-f/infrastructure-react":"^1.0.4","@c-a-f/infrastructure-vue":"^1.0.0","@c-a-f/i18n":"1.0.0","@c-a-f/permission":"1.0.0","@c-a-f/validation":"1.0.3","@c-a-f/workflow":"1.0.1"},"peerDependencies":{"@angular/core":">=18.0.0","@testing-library/react":">=14.0.0","@vue/test-utils":">=2.0.0","react":">=16.8.0","vue":">=3.0.0"},"peerDependenciesMeta":{"@angular/core":{"optional":true},"@testing-library/react":{"optional":true},"@vue/test-utils":{"optional":true},"react":{"optional":true},"vue":{"optional":true}},"devDependencies":{"@testing-library/react":"^16.0.0","happy-dom":"^15.11.7","react":"^18.2.0","vitest":"^2.1.0"},"_id":"@c-a-f/testing@1.0.2","gitHead":"c754b6d68b1a5b1196bb202e70c962ea7c3de87e","bugs":{"url":"https://github.com/ialiaslani/caf/issues"},"_nodeVersion":"22.13.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-6KAivYtgWlA9KIqJBddNJTRZ9Rt0VDBTbK9E3l0pgS1e6lugkFh187F0uYgx5pYPR+34zWSbN91ZbZrfWTS0Rw==","shasum":"dc8cb1f83ea321989f22b6b739eaf9ee69275dbe","tarball":"https://registry.npmjs.org/@c-a-f/testing/-/testing-1.0.2.tgz","fileCount":70,"unpackedSize":116169,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIA52xuTSRkFHpjIgdGbLikLxl2J/FHTXAcFWCjRn8rwvAiEAzt1QDkOO4MGvk6ifjVGUP6NiRsUMY460sGk+w0cA1P0="}]},"_npmUser":{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"},"directories":{},"maintainers":[{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/testing_1.0.2_1771680030106_0.009169020256264426"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-21T11:09:29.763Z","modified":"2026-02-21T13:20:30.388Z","1.0.1":"2026-02-21T11:09:30.006Z","1.0.2":"2026-02-21T13:20:30.266Z"},"bugs":{"url":"https://github.com/ialiaslani/caf/issues"},"author":{"name":"ali aslani","email":"aliaslani.mm@gmail.com"},"license":"MIT","homepage":"https://docs-caf.vercel.app/docs/packages/testing","keywords":["clean-architecture-frontend","clean-architecture","caf","testing","test-utils","mocks","test-helpers"],"repository":{"type":"git","url":"git+https://github.com/ialiaslani/caf.git"},"description":"Testing utilities and helpers for CAF applications. Provides mocks, test helpers, and utilities for testing UseCase, Ploc, Pulse, Workflow, Permission, I18n, and Validation.","maintainers":[{"name":"ialiaslani","email":"aliaslani.mm@gmail.com"}],"readme":"# @c-a-f/testing\r\n\r\nTesting utilities and helpers for CAF applications. Provides mocks, test helpers, and utilities for testing UseCase, Ploc, Pulse, Workflow, Permission, I18n, and Validation.\r\n\r\n**Documentation:** [@c-a-f/testing docs](https://docs-caf.vercel.app/docs/packages/testing)\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @c-a-f/testing --save-dev\r\n```\r\n\r\n## Usage\r\n\r\n### Core Testing Utilities\r\n\r\n#### Testing Ploc\r\n\r\n```typescript\r\nimport { createPlocTester, waitForStateChange } from '@c-a-f/testing/core';\r\nimport { Ploc } from '@c-a-f/core';\r\n\r\nclass CounterPloc extends Ploc<number> {\r\n  constructor() {\r\n    super(0);\r\n  }\r\n  increment() {\r\n    this.changeState(this.state + 1);\r\n  }\r\n}\r\n\r\ndescribe('CounterPloc', () => {\r\n  it('tracks state changes', () => {\r\n    const ploc = new CounterPloc();\r\n    const tester = createPlocTester(ploc);\r\n\r\n    ploc.increment();\r\n    ploc.increment();\r\n\r\n    expect(tester.getStateChangeCount()).toBe(2);\r\n    expect(tester.getStateHistory()).toEqual([0, 1, 2]);\r\n    expect(tester.getLastStateChange()).toBe(2);\r\n\r\n    tester.cleanup();\r\n  });\r\n\r\n  it('waits for state change', async () => {\r\n    const ploc = new CounterPloc();\r\n    \r\n    setTimeout(() => ploc.increment(), 100);\r\n    const finalState = await waitForStateChange(ploc, (state) => state === 1);\r\n\r\n    expect(finalState).toBe(1);\r\n  });\r\n});\r\n```\r\n\r\n#### Testing Pulse\r\n\r\n```typescript\r\nimport { createPulseTester, waitForPulseValue } from '@c-a-f/testing/core';\r\nimport { pulse } from '@c-a-f/core';\r\n\r\ndescribe('Pulse', () => {\r\n  it('tracks value changes', () => {\r\n    const count = pulse(0);\r\n    const tester = createPulseTester(count);\r\n\r\n    count.value = 5;\r\n    count.value = 10;\r\n\r\n    expect(tester.getValueChangeCount()).toBe(2);\r\n    expect(tester.getValueHistory()).toEqual([0, 5, 10]);\r\n    expect(tester.getLastValueChange()).toBe(10);\r\n\r\n    tester.cleanup();\r\n  });\r\n\r\n  it('waits for pulse value', async () => {\r\n    const count = pulse(0);\r\n    \r\n    setTimeout(() => { count.value = 5; }, 100);\r\n    const finalValue = await waitForPulseValue(count, (value) => value === 5);\r\n\r\n    expect(finalValue).toBe(5);\r\n  });\r\n});\r\n```\r\n\r\n#### Testing UseCase\r\n\r\n```typescript\r\nimport {\r\n  createMockUseCase,\r\n  createMockUseCaseSuccess,\r\n  createMockUseCaseError,\r\n  createUseCaseTester,\r\n  createSuccessResult,\r\n} from '@c-a-f/testing/core';\r\nimport { UseCase } from '@c-a-f/core';\r\n\r\ndescribe('UseCase', () => {\r\n  it('creates and tests mock use case', async () => {\r\n    const mockUseCase = createMockUseCase<[], string>(() =>\r\n      createSuccessResult('result')\r\n    );\r\n    const tester = createUseCaseTester(mockUseCase);\r\n\r\n    const result = await tester.execute([]);\r\n    expect(result.data.value).toBe('result');\r\n    expect(result.error.value).toBeNull();\r\n  });\r\n\r\n  it('uses success shortcut', async () => {\r\n    const useCase = createMockUseCaseSuccess([{ id: '1', name: 'John' }]);\r\n    const result = await useCase.execute();\r\n    expect(result.data.value).toEqual([{ id: '1', name: 'John' }]);\r\n  });\r\n\r\n  it('uses error shortcut', async () => {\r\n    const useCase = createMockUseCaseError(new Error('Failed'));\r\n    const result = await useCase.execute();\r\n    expect(result.error.value?.message).toBe('Failed');\r\n  });\r\n\r\n  it('executes and gets data', async () => {\r\n    const mockUseCase = createMockUseCase<[number], number>((count) =>\r\n      createSuccessResult(count * 2)\r\n    );\r\n    const tester = createUseCaseTester(mockUseCase);\r\n\r\n    const data = await tester.executeAndGetData([5]);\r\n    expect(data).toBe(10);\r\n  });\r\n});\r\n```\r\n\r\n#### Mock Ploc and state history (snapshot)\r\n\r\n```typescript\r\nimport {\r\n  createMockPloc,\r\n  createPlocTester,\r\n  assertStateHistory,\r\n  getStateHistorySnapshot,\r\n} from '@c-a-f/testing/core';\r\n\r\nit('mock Ploc and state history', () => {\r\n  const ploc = createMockPloc({ count: 0 });\r\n  const tester = createPlocTester(ploc);\r\n\r\n  ploc.changeState({ count: 1 });\r\n  ploc.changeState({ count: 2 });\r\n\r\n  assertStateHistory(tester, [{ count: 0 }, { count: 1 }, { count: 2 }]);\r\n  expect(getStateHistorySnapshot(tester)).toMatchSnapshot();\r\n  tester.cleanup();\r\n});\r\n```\r\n\r\n#### Mock Repository (domain I*Repository)\r\n\r\n```typescript\r\nimport { createMockRepository, createMockRepositoryStub } from '@c-a-f/testing/core';\r\nimport type { IUserRepository } from '../domain';\r\n\r\nit('mocks repository', async () => {\r\n  const repo = createMockRepository<IUserRepository>({\r\n    getUsers: async () => [{ id: '1', name: 'John' }],\r\n    getUserById: async (id) => ({ id, name: 'User ' + id }),\r\n  });\r\n\r\n  expect(await repo.getUsers()).toHaveLength(1);\r\n  expect((await repo.getUserById('2'))?.name).toBe('User 2');\r\n});\r\n\r\nit('stub and spy', async () => {\r\n  const stub = createMockRepositoryStub<IUserRepository>();\r\n  stub.getUsers = vi.fn().mockResolvedValue([]);\r\n  await stub.getUsers();\r\n  expect(stub.getUsers).toHaveBeenCalledTimes(1);\r\n});\r\n```\r\n\r\n#### Integration test helpers\r\n\r\n```typescript\r\nimport {\r\n  createPlocUseCaseContext,\r\n  flushPromises,\r\n} from '@c-a-f/testing/core';\r\n\r\nit('integration context', async () => {\r\n  const { ploc, useCase } = createPlocUseCaseContext(\r\n    { items: [], loading: false },\r\n    [{ id: '1', name: 'Item' }]\r\n  );\r\n  // Use with CAFProvider or pass as props\r\n  expect(ploc.state.loading).toBe(false);\r\n  const result = await useCase.execute();\r\n  expect(result.data.value).toHaveLength(1);\r\n  await flushPromises();\r\n});\r\n```\r\n\r\n### React Testing Utilities (`@c-a-f/testing/react`)\r\n\r\nUse these when testing React components that use `usePlocFromContext`, `useUseCaseFromContext`, or `usePloc` / `useUseCase` with context-provided instances.\r\n\r\n**Requirements:** `react` and `@testing-library/react` (peer dependencies). Install in your app:\r\n\r\n```bash\r\nnpm install react @testing-library/react\r\n```\r\n\r\n#### renderWithCAF\r\n\r\nRender a component wrapped in `CAFProvider` so Ploc/UseCase context is available:\r\n\r\n```tsx\r\nimport React from 'react';\r\nimport { screen } from '@testing-library/react';\r\nimport { renderWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/react';\r\nimport { usePlocFromContext, usePloc } from '@c-a-f/infrastructure-react';\r\n\r\nconst Counter = () => {\r\n  const ploc = usePlocFromContext('counter');\r\n  if (!ploc) return null;\r\n  const [state] = usePloc(ploc);\r\n  return <span data-testid=\"count\">{state.count}</span>;\r\n};\r\n\r\nit('renders with CAF context', () => {\r\n  const ploc = createTestPloc({ count: 5 });\r\n  renderWithCAF(<Counter />, { plocs: { counter: ploc } });\r\n  expect(screen.getByTestId('count')).toHaveTextContent('5');\r\n});\r\n```\r\n\r\n#### createTestPloc\r\n\r\nCreate a Ploc with controllable state (no business logic). Same idea as `createMockPloc` from core, for use in React tests:\r\n\r\n```tsx\r\nconst ploc = createTestPloc({ count: 0 });\r\nrenderWithCAF(<Counter />, { plocs: { counter: ploc } });\r\nploc.changeState({ count: 1 });\r\nexpect(screen.getByTestId('count')).toHaveTextContent('1');\r\n```\r\n\r\n#### waitForPlocState\r\n\r\nWait for a Ploc to reach a state matching a predicate (e.g. after an async update):\r\n\r\n```tsx\r\nconst ploc = createTestPloc({ loading: true, items: [] });\r\nrenderWithCAF(<List />, { plocs: { list: ploc } });\r\nploc.changeState({ loading: false, items: [{ id: '1' }] });\r\nawait waitForPlocState(ploc, (state) => !state.loading && state.items.length > 0);\r\nexpect(screen.getByText('1')).toBeInTheDocument();\r\n```\r\n\r\n#### mockUseCase\r\n\r\nCreate mock UseCases for context:\r\n\r\n```tsx\r\nconst submit = mockUseCase.success({ id: '1' });\r\nconst load = mockUseCase.error(new Error('Network error'));\r\nconst search = mockUseCase.async([{ id: '1' }], 50); // resolves after 50ms\r\n\r\nrenderWithCAF(<Form />, { useCases: { submit, load, search } });\r\n```\r\n\r\n- `mockUseCase.success(data)` — always returns success with `data`\r\n- `mockUseCase.error(error)` — always returns `error`\r\n- `mockUseCase.async(data, delayMs?)` — resolves with `data` after optional delay\r\n- `mockUseCase.fn(implementation)` — custom implementation (same as `createMockUseCase` from core)\r\n\r\n### Vue Testing Utilities (`@c-a-f/testing/vue`)\r\n\r\nUse these when testing Vue components that use `usePlocFromContext`, `useUseCaseFromContext`, or `useCAFContext`.\r\n\r\n**Requirements:** `vue` and `@vue/test-utils` (peer dependencies). Install in your app:\r\n\r\n```bash\r\nnpm install vue @vue/test-utils\r\n```\r\n\r\n#### mountWithCAF\r\n\r\nMount a component with CAF context (Plocs/UseCases) so inject works:\r\n\r\n```ts\r\nimport { mount } from '@vue/test-utils';\r\nimport { mountWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/vue';\r\nimport { usePlocFromContext, usePloc } from '@c-a-f/infrastructure-vue';\r\n\r\nconst Counter = defineComponent({\r\n  setup() {\r\n    const ploc = usePlocFromContext('counter');\r\n    const [state] = usePloc(ploc!);\r\n    return () => h('span', { 'data-testid': 'count' }, state.count);\r\n  },\r\n});\r\n\r\nit('renders with CAF context', () => {\r\n  const ploc = createTestPloc({ count: 5 });\r\n  const wrapper = mountWithCAF(Counter, { plocs: { counter: ploc } });\r\n  expect(wrapper.get('[data-testid=\"count\"]').text()).toBe('5');\r\n});\r\n```\r\n\r\n#### createTestPloc / waitForPlocState / mockUseCase\r\n\r\nSame as React: use `createTestPloc`, `waitForPlocState`, and `mockUseCase` with `mountWithCAF` for Vue component tests.\r\n\r\n### Angular Testing Utilities (`@c-a-f/testing/angular`)\r\n\r\nUse these when testing Angular components that use `injectPlocFromContext`, `injectUseCaseFromContext`, or `injectCAFContext`.\r\n\r\n**Requirements:** `@angular/core` and `@c-a-f/infrastructure-angular` (peer/dependency). Install in your app:\r\n\r\n```bash\r\nnpm install @angular/core @c-a-f/infrastructure-angular\r\n```\r\n\r\n#### provideTestingCAF\r\n\r\nProvide CAF context in `TestBed` so injection works:\r\n\r\n```ts\r\nimport { TestBed } from '@angular/core/testing';\r\nimport { provideTestingCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/angular';\r\n\r\nconst ploc = createTestPloc({ count: 0 });\r\nawait TestBed.configureTestingModule({\r\n  imports: [MyComponent],\r\n  providers: [\r\n    provideTestingCAF({\r\n      plocs: { counter: ploc },\r\n      useCases: { submit: mockUseCase.success({ id: '1' }) },\r\n    }),\r\n  ],\r\n}).compileComponents();\r\n\r\nconst fixture = TestBed.createComponent(MyComponent);\r\n```\r\n\r\n#### createTestPloc / waitForPlocState / mockUseCase\r\n\r\nSame as React/Vue: use `createTestPloc`, `waitForPlocState`, and `mockUseCase` with `provideTestingCAF` in your Angular tests.\r\n\r\n#### Testing RouteManager\r\n\r\n```typescript\r\nimport { createMockRouteRepository, createRouteManagerTester } from '@c-a-f/testing/core';\r\nimport { RouteManager } from '@c-a-f/core';\r\n\r\ndescribe('RouteManager', () => {\r\n  it('tracks route changes', async () => {\r\n    const mockRepo = createMockRouteRepository();\r\n    const routeManager = new RouteManager(mockRepo);\r\n    const tester = createRouteManagerTester(routeManager);\r\n\r\n    await tester.changeRoute('/dashboard');\r\n    expect(tester.getCurrentRoute()).toBe('/dashboard');\r\n    expect(tester.getRouteHistory()).toContain('/dashboard');\r\n  });\r\n});\r\n```\r\n\r\n### Workflow Testing Utilities\r\n\r\n```typescript\r\nimport { createWorkflowTester, waitForWorkflowState } from '@c-a-f/testing/workflow';\r\nimport { WorkflowManager, WorkflowDefinition } from '@c-a-f/workflow';\r\n\r\ndescribe('Workflow', () => {\r\n  it('tracks workflow state changes', async () => {\r\n    const workflow = new WorkflowManager(definition);\r\n    const tester = createWorkflowTester(workflow);\r\n\r\n    await tester.dispatch('approve');\r\n    await waitForWorkflowState(workflow, 'approved');\r\n\r\n    expect(tester.getCurrentState()).toBe('approved');\r\n    expect(tester.getStateHistory().length).toBeGreaterThan(1);\r\n\r\n    tester.cleanup();\r\n  });\r\n\r\n  it('waits for final state', async () => {\r\n    const workflow = new WorkflowManager(definition);\r\n    \r\n    await workflow.dispatch('complete');\r\n    const finalState = await waitForFinalState(workflow);\r\n\r\n    expect(finalState.isFinal).toBe(true);\r\n  });\r\n});\r\n```\r\n\r\n### Permission Testing Utilities\r\n\r\n```typescript\r\nimport { createMockPermissionChecker, createPermissionTester } from '@c-a-f/testing/permission';\r\nimport { PermissionManager } from '@c-a-f/permission';\r\n\r\ndescribe('Permission', () => {\r\n  it('tests permission checking', async () => {\r\n    const mockChecker = createMockPermissionChecker(['user.edit', 'post.create']);\r\n    const manager = new PermissionManager(mockChecker);\r\n    const tester = createPermissionTester(manager);\r\n\r\n    expect(await tester.hasPermission('user.edit')).toBe(true);\r\n    expect(await tester.hasPermission('user.delete')).toBe(false);\r\n    expect(await tester.hasAnyPermission(['user.edit', 'user.delete'])).toBe(true);\r\n  });\r\n\r\n  it('modifies permissions dynamically', () => {\r\n    const mockChecker = createMockPermissionChecker(['user.view']);\r\n    \r\n    mockChecker.addPermission('user.edit');\r\n    expect(mockChecker.check('user.edit').granted).toBe(true);\r\n\r\n    mockChecker.removePermission('user.view');\r\n    expect(mockChecker.check('user.view').granted).toBe(false);\r\n  });\r\n});\r\n```\r\n\r\n### I18n Testing Utilities\r\n\r\n```typescript\r\nimport { createMockTranslator, createTranslationTester } from '@c-a-f/testing/i18n';\r\nimport { TranslationManager } from '@c-a-f/i18n';\r\n\r\ndescribe('I18n', () => {\r\n  it('tests translation', () => {\r\n    const mockTranslator = createMockTranslator({\r\n      en: { greeting: 'Hello', welcome: 'Welcome {{name}}' },\r\n      fa: { greeting: 'سلام', welcome: 'خوش آمدید {{name}}' },\r\n    });\r\n    const manager = new TranslationManager(mockTranslator);\r\n    const tester = createTranslationTester(manager);\r\n\r\n    expect(tester.t('greeting')).toBe('Hello');\r\n    expect(tester.translateWithValues('welcome', { name: 'John' })).toBe('Welcome John');\r\n  });\r\n\r\n  it('tests language switching', async () => {\r\n    const mockTranslator = createMockTranslator({\r\n      en: { greeting: 'Hello' },\r\n      fa: { greeting: 'سلام' },\r\n    });\r\n    const manager = new TranslationManager(mockTranslator);\r\n    const tester = createTranslationTester(manager);\r\n\r\n    expect(tester.getCurrentLanguage()).toBe('en');\r\n    await tester.changeLanguage('fa');\r\n    expect(tester.getCurrentLanguage()).toBe('fa');\r\n    expect(tester.t('greeting')).toBe('سلام');\r\n  });\r\n});\r\n```\r\n\r\n### Validation Testing Utilities\r\n\r\n```typescript\r\nimport { createMockValidator, createValidationTester } from '@c-a-f/testing/validation';\r\n\r\ndescribe('Validation', () => {\r\n  it('tests validation', async () => {\r\n    const mockValidator = createMockValidator((data: any) => {\r\n      return data.email && data.email.includes('@');\r\n    });\r\n    const tester = createValidationTester(mockValidator);\r\n\r\n    const successResult = await tester.expectSuccess({ email: 'test@example.com' });\r\n    expect(successResult.email).toBe('test@example.com');\r\n\r\n    const errors = await tester.expectFailure({ email: 'invalid' });\r\n    expect(errors.length).toBeGreaterThan(0);\r\n  });\r\n});\r\n```\r\n\r\n## Exports\r\n\r\n### Core Testing (`@c-a-f/testing/core`)\r\n\r\n- `PlocTester` — Tester for Ploc instances\r\n- `createPlocTester` — Create a Ploc tester\r\n- `createMockPloc` — Create a Ploc with controllable state (no logic)\r\n- `MockPloc` — Concrete Ploc class for tests\r\n- `waitForStateChange` — Wait for state change matching predicate\r\n- `waitForStateChanges` — Wait for specific number of state changes\r\n- `assertStateHistory` — Assert state history matches expected (snapshot-style)\r\n- `getStateHistorySnapshot` / `getStateHistorySnapshotJson` — Snapshot state history\r\n- `PulseTester` — Tester for Pulse instances\r\n- `createPulseTester` — Create a Pulse tester\r\n- `waitForPulseValue` — Wait for pulse value matching predicate\r\n- `MockUseCase` — Mock UseCase implementation\r\n- `createMockUseCase` — Create a mock UseCase\r\n- `createMockUseCaseSuccess` — Mock UseCase that returns success with data\r\n- `createMockUseCaseError` — Mock UseCase that returns an error\r\n- `createMockUseCaseAsync` — Mock UseCase that resolves after optional delay\r\n- `UseCaseTester` — Tester for UseCase instances\r\n- `createUseCaseTester` — Create a UseCase tester\r\n- `createSuccessResult` — Create successful RequestResult\r\n- `createErrorResult` — Create failed RequestResult\r\n- `createLoadingResult` — Create loading RequestResult\r\n- `createMockRepository` — Generic stub for domain I*Repository interfaces\r\n- `createMockRepositoryStub` — Empty repository stub (assign or spy methods)\r\n- `flushPromises` — Resolve pending microtasks in integration tests\r\n- `runWithFakeTimers` — Placeholder for running code with fake timers\r\n- `createPlocUseCaseContext` — Minimal Ploc + UseCase context for integration tests\r\n- `MockRouteRepository` — Mock RouteRepository implementation\r\n- `createMockRouteRepository` — Create a mock RouteRepository\r\n- `RouteManagerTester` — Tester for RouteManager instances\r\n- `createRouteManagerTester` — Create a RouteManager tester\r\n\r\n### Workflow Testing (`@c-a-f/testing/workflow`)\r\n\r\n- `WorkflowTester` — Tester for WorkflowManager instances\r\n- `createWorkflowTester` — Create a Workflow tester\r\n- `waitForWorkflowState` — Wait for workflow to reach specific state\r\n- `waitForFinalState` — Wait for workflow to reach final state\r\n\r\n### Permission Testing (`@c-a-f/testing/permission`)\r\n\r\n- `MockPermissionChecker` — Mock PermissionChecker implementation\r\n- `createMockPermissionChecker` — Create a mock PermissionChecker\r\n- `PermissionTester` — Tester for PermissionManager instances\r\n- `createPermissionTester` — Create a Permission tester\r\n\r\n### I18n Testing (`@c-a-f/testing/i18n`)\r\n\r\n- `MockTranslator` — Mock Translator implementation\r\n- `createMockTranslator` — Create a mock Translator\r\n- `TranslationTester` — Tester for TranslationManager instances\r\n- `createTranslationTester` — Create a Translation tester\r\n\r\n### Validation Testing (`@c-a-f/testing/validation`)\r\n\r\n- `MockValidator` — Mock Validator implementation\r\n- `createMockValidator` — Create a mock Validator\r\n- `ValidationTester` — Tester for Validator instances\r\n- `createValidationTester` — Create a Validation tester\r\n- `expectSuccess` — Validate and expect success\r\n- `expectFailure` — Validate and expect failure\r\n\r\n### React Testing (`@c-a-f/testing/react`)\r\n\r\n- `renderWithCAF` — Render with CAFProvider (Ploc/UseCase context)\r\n- `RenderWithCAFOptions` — Options for renderWithCAF (plocs, useCases, and RTL options)\r\n- `createTestPloc` — Create a test Ploc with controllable state\r\n- `waitForPlocState` — Wait for Ploc state to match a predicate\r\n- `mockUseCase` — Mock UseCase helpers: `success`, `error`, `async`, `fn`\r\n\r\n### Vue Testing (`@c-a-f/testing/vue`)\r\n\r\n- `mountWithCAF` — Mount with CAF context (Ploc/UseCase provide)\r\n- `MountWithCAFOptions` — Options for mountWithCAF (plocs, useCases, and Vue Test Utils options)\r\n- `createTestPloc` — Create a test Ploc with controllable state\r\n- `waitForPlocState` — Wait for Ploc state to match a predicate\r\n- `mockUseCase` — Mock UseCase helpers: `success`, `error`, `async`, `fn`\r\n\r\n### Angular Testing (`@c-a-f/testing/angular`)\r\n\r\n- `provideTestingCAF` — Provide CAF context for TestBed (plocs, useCases)\r\n- `ProvideTestingCAFConfig` — Config for provideTestingCAF\r\n- `createTestPloc` — Create a test Ploc with controllable state\r\n- `waitForPlocState` — Wait for Ploc state to match a predicate\r\n- `mockUseCase` — Mock UseCase helpers: `success`, `error`, `async`, `fn`\r\n\r\n## Dependencies\r\n\r\n- `@c-a-f/core` — Core primitives\r\n- `@c-a-f/infrastructure-react` — React provider (for `@c-a-f/testing/react`)\r\n- `@c-a-f/infrastructure-vue` — Vue provider (for `@c-a-f/testing/vue`)\r\n- `@c-a-f/infrastructure-angular` — Angular provider (for `@c-a-f/testing/angular`)\r\n- `@c-a-f/workflow` — Workflow package (for workflow testing utilities)\r\n- `@c-a-f/permission` — Permission package (for permission testing utilities)\r\n- `@c-a-f/i18n` — I18n package (for i18n testing utilities)\r\n- `@c-a-f/validation` — Validation package (for validation testing utilities)\r\n\r\n**Framework testing:** Optional peer dependencies per entry point:\r\n- `@c-a-f/testing/react`: `react`, `@testing-library/react`\r\n- `@c-a-f/testing/vue`: `vue`, `@vue/test-utils`\r\n- `@c-a-f/testing/angular`: `@angular/core`\r\n\r\n## Dev Dependencies\r\n\r\n- `vitest` — Testing framework (optional, works with any testing framework)\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md"}