Shiftly’s test suite is built on Vitest and the Testing Library family. Tests are split into two broad concerns: the pure domain layer — which implements all Israeli labor law pay rules independently of any framework — and the UI layer — which validates React components, hooks, layouts, providers, and routes. Because the domain layer is framework-agnostic, its tests run without any React or browser environment setup, making them fast and deterministic.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/dmaman86/shiftly/llms.txt
Use this file to discover all available pages before exploring further.
Testing Stack
| Package | Role |
|---|---|
vitest | Test runner, assertion library, and coverage orchestrator |
@testing-library/react | Render React components in a jsdom environment |
@testing-library/jest-dom | Extends Vitest expect with DOM matchers (.toBeInTheDocument(), etc.) |
@testing-library/user-event | Simulates realistic browser interactions (typing, clicking, etc.) |
jsdom | Provides a DOM environment for all tests |
src/test/setup.ts. It extends Vitest’s expect with jest-dom matchers and registers an afterEach cleanup hook that unmounts React trees between tests:
Test Directory Structure
All tests live undersrc/test/ and mirror the structure of the source code they cover:
Running Tests
Test Utilities
Three helper modules undersrc/test/ui/utils/ provide the scaffolding for UI tests.
test-utils.tsx — Custom render with all providers
Rather than importing render directly from @testing-library/react, UI tests use renderWithProviders from test-utils.tsx. This wraps any component under test with the full provider stack: Redux Provider, BrowserRouter, MUI ThemeProvider, LocalizationProvider, and SnackbarProvider.
withRouter, withTheme, withSnackbar), so a test for a purely presentational component can skip the router or snackbar without ceremony. A createMockStore helper creates a fresh Redux store per test, optionally seeded with preloadedState:
screen, within, waitFor, waitForElementToBeRemoved, fireEvent, act, cleanup, and userEvent so test files have a single import point.
mock-factories.ts — Factory functions for test data
mock-factories.ts exports typed factory functions that produce valid Redux state objects with sensible defaults. Tests can override individual fields without constructing the full shape manually:
setup-domain.ts — Domain instance setup for UI tests
UI tests that depend on Redux slices which call into the domain pipeline need a consistent domain mock. setup-domain.ts builds a single PayMapPipeline instance once via buildPayMapPipeline(), then uses vi.mock inside a beforeAll hook to replace the @/app module with that instance before any test in the suite runs:
mock-factories.ts imports pipelineInstance from this module so factory-generated state objects use the same pipeline instance as the mocked Redux slices.
Domain Layer Testing
The domain layer is entirely framework-agnostic. Calculators, builders, reducers, resolvers, and services have no React or Redux dependencies, so they are imported and called directly in tests — no providers, no stores, no rendering. This makes domain tests the fastest and most reliable part of the suite.Key principle
All pay-rule logic lives in plain TypeScript classes and functions. Tests importbuildPayMapPipeline() directly, call its methods, and assert on the returned data structures — exactly the same way the Redux slices consume the domain at runtime.
The End-to-End Salary Pipeline Test
src/test/domain/e2e/salary-pipeline.e2e.test.ts is the most comprehensive test file in the codebase. It validates the complete calculation flow from a raw Shift input through to the monthly aggregate structure by exercising the pipeline at every layer.
The test suite constructs a fresh PayMapPipeline before each test using buildPayMapPipeline() and covers the following scenarios:
Pipeline component availability — verifies that every builder, resolver, and service is initialised and that each call to buildPayMapPipeline() produces a distinct instance.
Shift layer — individual shift processing:
- A standard 8-hour regular shift produces 8 hours at 100%, 0 at 125%, 0 at 150%.
- A 10-hour shift splits into 8 hours at 100% and 2 hours at 125% (first overtime tier).
- A 12-hour shift produces the full 8 / 2 / 2 split across all three rate tiers.
- Night shifts crossing midnight (e.g., 22:00 – 06:00) calculate the correct total duration.
- Duty shifts (
isDuty: true) populateperDiemShiftwithisFieldDutyShift: trueand the correct hours count. - Shabbat shifts (Saturday,
WorkDayType.SpecialFull) produce populatedspecialhour buckets.
workDaysForMonthBuilder.build({ year, month, eventMap })returns the correct number of days for January (31), February in a leap year (29), and February in a non-leap year (28).- All Saturdays in the month are classified as
WorkDayType.SpecialFull. - Friday days are correctly identified by their Hebrew day label (
"ו").
monthPayMapCalculator.createEmpty()returns aMonthPayMapwith all numeric fields zeroed.- The full structure includes
regular,extra,special,hours100Sick,hours100Vacation,extra100Shabbat,perDiem,mealAllowance, andtotalHours.
- A single shift processed end-to-end (build month → find work day → build shift map) produces the expected hour breakdown.
- A realistic work week (Mon–Thu regular + Thursday overtime) yields correct per-day and aggregate totals.
- A mixed month scenario (regular days, overtime, night shifts) processes without errors.
- Year-boundary shifts (Dec 31 → Jan 1) calculate the correct 8-hour total.
- Shifts on February 29 in a leap year are handled correctly.
- Month lengths of 30 and 31 days are verified at both ends of the date range.
CI Pipeline
The GitHub Actions workflow at.github/workflows/ci.yml runs on every push to main or master. It provisions a Bun environment, installs dependencies with a frozen lockfile, and then executes the quality gate sequence:
coverage-final.json report generated by Vitest’s V8 provider. The build step runs last so that a failing test or type error blocks the build artifact from being produced.