### Bash: Example File Structure for Tests Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/overview.en.md This example shows a typical file structure where tests (user-repository.test.ts) are located next to the source file (user-repository.ts) they are testing, promoting maintainability. ```bash ├── data/ | └── repositories/ | | └── user-repository/ | | | ├── user-repository.ts | | | ├── user-repository.test.ts | | | └── index.ts ``` -------------------------------- ### Application Structure Example (React) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Provides an example of the 'Application' layer structure for a React project. This layer handles environment-specific details, framework dependencies, application routing, and service initialization. ```typescript ├── app/ | ├── routing/ | └── app.tsx ├── screens/ ├── modules/ ├── data/ └── shared/ ``` -------------------------------- ### React Feature Structure Example Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/features/overview.en.md Provides an example of a feature's directory structure within a React-based Redstack application, highlighting component files, tests, styles, and utility files. ```tree ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── payment-switch/ | | | ├── payment-switch.tsx | | | ├── payment-switch.test.tsx | | | ├── switch-btn/ | | | ├── styles.ts | | | ├── utils/ | | | ├── ui-store/ | | | ├── constants.ts | | | ├── types.ts | | | └── index.ts | | ├── card-payment/ | | ├── cash-payment/ | | └── index.ts | ├── domain/ | └── index.ts ├── data/ └── shared/ ``` -------------------------------- ### Domain Directory Structure Example 1 Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/modules/domain.en.md This example shows a typical domain directory structure where enums, types, and constants are placed in separate files within the domain directory. ```bash ├── domain/ | ├── utils/ | ├── stores/ | ├── validations/ | ├── enums/ | ├── types/ | ├── constants/ | └── index.ts ``` -------------------------------- ### Test Case Example from Requirements Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/test-case-formatting.en.md Provides an example of how task requirements, including steps and conditional logic, can be translated into test cases using TypeScript syntax. ```tsx # Shopping Cart Payment ## Main Scenario 1. User added goods to cart 2. User went to cart 3. System displays list of goods added to cart 4. If user has saved card, then clicking "Pay" button: 1. Payment of goods through saved card starts in background 2. System displays modal window to show payment status 5. System redirects user to popular goods page ``` -------------------------------- ### Project Structure Example Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md Illustrates a typical project structure for a CartStore module, highlighting internal utilities and services that should not be substituted during testing. ```jsx ├── cart-store/ | ├── cart-store.ts | ├── cart-store.test.ts | ├── utils | | ├── format-item-to-view | | | ├── format-item-to-view.ts | | | └── index.ts | | └── index.ts | ├── services | | ├── cart-manager | | | ├── cart-manager.ts | | | └── index.ts | | └── index.ts | └── index.ts ``` -------------------------------- ### Kebab-Case Naming Convention Source: https://github.com/redstack-dev/docs/blob/main/style-guides/react/created.en.md Illustrates the correct kebab-case naming convention for component directories and files, contrasting it with invalid camelCase examples. ```directory-structure ├── user-info/ | ├── user-info.tsx | └── index.ts ├── button-group/ | ├── button-group.tsx | └── index.ts ``` ```directory-structure ❌ Invalid ├── userInfo/ | ├── userInfo.tsx | └── index.ts ├── buttonGroup/ | ├── buttonGroup.tsx | └── index.ts ``` -------------------------------- ### Example Project Structure Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Illustrates the basic directory structure of a project using the Redstack architecture, showing top-level folders like app, screens, modules, data, and shared. ```tree ├── app/ ├── screens/ ├── modules/ ├── data/ └── shared/ ``` -------------------------------- ### Domain Directory Structure Example 2 Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/modules/domain.en.md This example illustrates an alternative domain directory structure where enums, types, and constants are consolidated into single files (e.g., enums.ts, types.ts, constants.ts) within the domain directory. ```bash ├── domain/ | ├── utils/ | ├── stores/ | ├── validations/ | ├── enums.ts | ├── types.ts | ├── constants.ts | └── index.ts ``` -------------------------------- ### Valid Function and Method Names (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples of correctly named functions and methods that start with a verb, indicating the action performed. ```TypeScript function calcSum() {} function getUserData() {} function removeID() {} function refreshData() {} function formatToView() {} function createRequest() {} ``` ```TypeScript class Example { public calcSum = () => {} public getUserData = () => {} public removeID = () => {} public refresh = () => {} public formatToView = () => {} } ``` -------------------------------- ### Using 'Delete' for Infrastructure Code (Conceptual) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Conceptual examples highlighting the recommendation to use 'Delete' for infrastructure code, with 'Remove' as a fallback for semantic exceptions. ```string `Remove` is used only in situations where `Delete` is not suitable semantically. ``` -------------------------------- ### Application Structure Example (Next.js) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Illustrates the 'Application' layer structure within a Next.js project, highlighting how the 'pages' directory serves as the application layer due to Next.js specific conventions. ```typescript ├── pages/ # pages is the application layer (Next.js specificity) | ├── index.tsx | ├── _app.tsx ├── screens/ ├── modules/ ├── data/ └── shared/ ``` -------------------------------- ### Example Project Structure Source: https://github.com/redstack-dev/docs/blob/main/architecture/intro.en.md Demonstrates a typical directory structure for a frontend project following the Redstack architectural approach. This structure helps organize code by feature and layer, promoting maintainability and understanding. ```bash ├── app/ ├── screens/ ├── modules/ ├── data/ └── shared/ ``` -------------------------------- ### Module Structure Example - TypeScript Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/overview.en.md Illustrates a typical directory structure for a module within the application, including features, domain logic, and API definition files. ```typescript ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── features/ | | ├── domain/ | | └── index.ts ├── data/ └── shared/ ``` -------------------------------- ### Mocking Environment-Interacting Dependencies in CreateBookScreenStore Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md Demonstrates substituting dependencies that interact with the environment, such as Router and notifyService, in a CreateBookScreenStore. This example tests the redirection logic after successful book creation. ```tsx export class CreateBookScreenStore { constructor( private readonly administrationRepository: AdministrationRepository, private readonly routerService: Router, private readonly notifyService: typeof notify, ) {} } it('Successful book creation redirects to books list page', async () => { const fakeBookFormValues = makeFakeBookFormValues() const adminRepositoryMock = mock() const routerMock = createRouterMock() const notifyMock = mock() const sut = new CreateBookScreenStore( adminRepositoryMock, routerMock, notifyMock, ) await sut.createBook(fakeBookFormValues) expect(routerMock).toMatchObject({ pathname: APP_ROUTES.books.getRedirectPath(), }) }) ``` -------------------------------- ### Domain Utils: Cart Item Formatting Example Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/domain.en.md Provides an example of a utility function within the domain layer for formatting cart item data. It transforms raw cart item data into a more display-friendly format. ```ts // domain/utils/cart-utils.ts export function formatCartItem(item: CartItem): FormattedCartItem { return { ...item, formattedPrice: `${item.price} rub.`, totalPrice: item.price * item.quantity } } ``` -------------------------------- ### Module Feature Structure Example Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/features/overview.en.md Illustrates a typical directory structure for a feature within a module, showcasing the separation of concerns like domain logic and feature implementation. ```tree ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── features/ | | | ├── payment-switch/ | | | ├── card-payment/ | | | ├── cash-payment/ | | | └── index.ts | | ├── domain/ | | └── index.ts ├── data/ └── shared/ ``` -------------------------------- ### Interact with Browser API via Abstraction in UIStore Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/features/ui-store.en.md Explains the practice of abstracting Browser API interactions within `UIStore` to facilitate easier testing with mock dependencies. Examples include using `LocalStorageService` and `IntersectionObserver` abstractions. ```ts export class UIStore { constructor( private readonly storage: LocalStorageService, ) { makeAutoObservable(this) } public setSearch = (search: string) => { this.storage.setItem('search', search) } } ``` ```ts export class UIStore { constructor( private readonly intersectionObserver: IntersectionObserver, ) { makeAutoObservable(this); } ... public mount = (itemRef: Ref) => { this.intersectionObserver(this.showAction, { root: itemRef.current }) } } ``` -------------------------------- ### Screens Structure Example (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Depicts the directory layout for the 'Screens' layer, where application screens are assembled from various features. This layer is responsible for integrating features and managing screen-specific routing interactions. ```typescript ├── app/ ├── screens/ | ├── feedback/ | ├── no-access/ | ├── not-found/ | ├── popular-goods/ | ├── new-goods/ | | ├── new-goods.tsx | | ├── store/ | | └── index.ts | └── index.ts ├── modules/ ├── data/ └── shared/ ``` -------------------------------- ### Feature Structure Example (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Illustrates the typical directory structure for a 'feature' within a module, including UI components, logic, styles, and other necessary entities. It emphasizes that UI components are solely for rendering, with all logic residing outside. ```typescript ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── features/ | | | ├── payment-switch/ | | | ├── card-payment/ | | | ├── cash-payment/ | | | └── index.ts | | ├── domain/ | | └── index.ts ├── data/ └── shared/ ``` -------------------------------- ### Mock/Stub Naming Convention (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/testing/test-double.en.md Demonstrates the use of the 'mock' postfix for test doubles that are either mocks or stubs. This convention simplifies test implementation by removing the need to differentiate between mocks and stubs during test setup. The example shows a mock of a notification service and a stub for cart network sources. ```ts describe('CreateBookScreenStore', () => { it('Successful book creation shows success notification', async () => { const notifyMock = mock() const fakeBookFormValues = makeFakeBookFormValues({ name: 'Clean Code' }) const sut = new CreateBookScreenStore(notifyMock) await sut.createBook(fakeBookFormValues) expect(notifyMock.success).toBeCalledWith('Clean Code successfully created') }) }) ``` ```ts describe('CartRepository', () => { it('Item list and counter reset after reset', () => { const cartSourcesMock = mock() const sut = new CartRepository(cartSourcesMock, createCacheService()) const goodsCountQuery = sut.getGoodsCountQuery() const goodsQuery = sut.getGoodsQuery() sut.resetCartCache() expect(goodsCountQuery.data).toBe(0) expect(goodsQuery.data).toEqual([]) }) }) ``` -------------------------------- ### Example Modules Layer Structure (Payment Subdomain) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Shows the structure of a 'Modules' layer, specifically for a 'payment' subdomain. This layer contains the core business logic and features of the application, divided into 'features' and 'domain' segments. ```tree ├── app/ ├── screens/ ├── modules/ | └── payment/ | | ├── features/ | | ├── domain/ | | └── index.ts ├── data/ └── shared/ ``` -------------------------------- ### Domain Structure Example (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Shows the organizational structure for the 'Domain' layer, which contains pure business logic, reusable logic, data interactions, types, and constants. It highlights the separation of domain-specific logic from shared utilities. ```typescript ├── app/ ├── screens/ ├── modules/ | └── cart/ | | ├── features/ | | └── domain/ | | | ├── services/ | | | ├── stores/ | | | | ├── cart-store/ | | | | └── index.ts | | | ├── utils/ | | | ├── types/ | | | ├── constants/ | | | └── index.ts | | └── index.ts ├── data/ └── shared/ ``` -------------------------------- ### Invalid Module Directory Structure (with 'Module' postfix) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/modules/creation.en.md Provides examples of invalid module directory names that include the 'Module' postfix, which is redundant and against the project's naming conventions. ```shell ├── modules/ | ├── cartModule/ | ├── pymentModule/ | ├── requestModule/ | └── layoutModule/ ``` -------------------------------- ### Module Dependency Management Example - TypeScript Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/overview.en.md Demonstrates how to manage module dependencies using 'index.ts' for public APIs and 'external.ts' for controlling incoming dependencies. ```typescript ├── app/ ├── screens/ ├── modules/ | ├── payment/ | | ├── features/ | | ├── domain/ | | ├── external.ts # Incoming dependencies | | └── index.ts # Module's public API ├── data/ └── shared/ ``` -------------------------------- ### Valid Props Naming (Lowercase Start) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/react/props.en.md Demonstrates the correct convention for naming props, which should all start with a lowercase letter. ```ts interface Props { title: string userName: string onClick: () => void footer: ReactNode header: FunctionComponent } ``` -------------------------------- ### Example Data Layer Structure Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Presents the directory structure for the 'Data' layer in a Redstack project. This layer is responsible for data retrieval, aggregation, caching, and formatting, and is typically divided into 'repositories' and 'sources' segments. ```tree ├── app/ ├── screens/ ├── modules/ ├── data/ | ├── repositories/ | ├── sources/ | └── index.ts └── shared/ ``` -------------------------------- ### Plural Noun Naming Conventions (tsx) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples of component names that clearly distinguish between singular and plural entities, using 'Screen' and 'ListScreen'. ```tsx function DraftScreen() {} function DraftListScreen() {} ``` -------------------------------- ### Format Price for Zero Input Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/antipatterns.en.md Demonstrates how to write a specific test case for a function that formats prices. The valid example clearly states that for a zero input, the function returns default text. ```tsx describe('formatPriceToView', () => { it('For zero returns default text', () => { expect(formatPriceToView(0)).toBe('Free') }) }) ``` -------------------------------- ### Invalid: Exporting URL from Non-constants File (TS) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/constants.en.md An invalid example where a constant `apiUrl` is defined and exported directly from a file that is not named `constants.ts`. ```ts export const apiUrl = 'https://domain.com' ``` -------------------------------- ### Using 'Remove' vs. 'Delete' for Business Logic (Conceptual) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Conceptual examples illustrating the use of 'Remove' for potentially restorable items and 'Delete' for permanent removal in business logic. ```string `removeAccount` - function for removing a user account with the possibility of restoring it in the future. `removeRequest` - function that removes an application from the registry but moves it to an "archive". `deleteRequest` - function that completely deletes an application from the system. ``` -------------------------------- ### Test CartStore Calculation (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/domain.en.md This example demonstrates unit testing a CartStore in TypeScript. It mocks the CartRepository and CartCalculator dependencies to verify that the store correctly calculates the total. ```TypeScript import { CartStore } from './cart-store'; import { CartRepository } from '../interfaces/cart-repository'; import { CartCalculator } from '../interfaces/cart-calculator'; describe('CartStore', () => { it('should calculate total correctly', () => { const cartRepositoryMock = mock() const cartCalculatorMock = mock({ calculateTotal: () => 100 }) const store = new CartStore(cartRepositoryMock, cartCalculatorMock) const total = store.total expect(total).toBe(100) }) }) ``` -------------------------------- ### DI Example for React Hook useBookForm Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md Illustrates Dependency Injection for a React hook named useBookForm. The hook accepts a store dependency as a parameter, promoting an abstract interface for better testability. ```tsx function useBookForm(store: BookFormStore) { const form = useForm() useEffect(() => { store.getBooks() }, []) return { isPresentCoAuthor: store.isPresentCoAuthor, submit: form.handleSubmit, } } ``` -------------------------------- ### Spy Naming Convention (TypeScript/React) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/testing/test-double.en.md Illustrates the convention of using the 'spy' postfix for spies. This helps distinguish spies from regular functions and other test doubles. The example shows a valid use case where an `onChange` handler is spied upon using `vi.fn()` and correctly named with the 'spy' postfix. An invalid example is also provided for contrast. ```tsx it('OnChange is called without passed isExpanded', async () => { const onChangeSpy = vi.fn() renderWithTheme( } onChange={onChangeSpy} > Content , ) const title = screen.getByRole('button', { name: 'Test' }) await userEvents.click(title) expect(onChangeSpy).toHaveBeenCalled() }) ``` ```tsx it('OnChange is called without passed isExpanded', async () => { const onChange = vi.fn() renderWithTheme( } onChange={onChange} > Content , ) const title = screen.getByRole('button', { name: 'Test' }) await userEvents.click(title) expect(onChange).toHaveBeenCalled() }) ``` -------------------------------- ### Invalid Function and Method Names (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples of incorrectly named functions and methods that do not follow the verb-first convention or are misleading. ```TypeScript // Verb at the end function sumCalc() {} // Judging by the name, this is not a function but an object function userDataGetter() {} // Judging by the name, this is not a function but an object function toViewFormatter() {} // The name indicates that this is a process, not a function function refreshingData() {} ``` -------------------------------- ### Boolean Return Function Naming (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples of functions that return a boolean, correctly prefixed with a verb like 'check' or 'validate'. ```TypeScript const checkIsEmpty = (value: unknown) => remeda.isEmpty(value) function checkWasRemoved(obj?: Record): obj is undefined { return obj === undefined } function validateID(id: string) { return id.length === 2 } ``` ```TypeScript class Example { public checkIsEmpty = (value: unknown) => remeda.isEmpty(value) public checkWasRemoved = (obj?: Record): obj is undefined => obj === undefined public validateID = (obj: { id?: string }) => Boolean(obj.id) } ``` -------------------------------- ### JS Class Directory Naming Convention Source: https://github.com/redstack-dev/docs/blob/main/style-guides/classes.en.md Directories containing JavaScript classes should start with a capital letter to clearly identify them as classes, improving readability and maintainability. ```directory-naming ├── services/ | |── FileService/ | | |── file-service.ts | | └── index.ts | |── ErrorService/ | | |── error-service.ts | | └── index.ts | └── index.ts ``` -------------------------------- ### Plural Noun Naming Conventions (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples demonstrating the convention of adding a clarifying word like 'List' to function names when dealing with plural nouns. ```TypeScript class Example { public getDraft = () => {} public getDraftList = () => {} public sendDraft = () => {} public sendDraftList = () => {} public calcDraftSum = () => {} public calcDraftListSum = () => {} } ``` -------------------------------- ### Private Helper Function Example Source: https://github.com/redstack-dev/docs/blob/main/style-guides/utils.en.md Shows an exception to the separate directory rule where private helper functions, like 'parseVersion', can be defined within the same file as the exported function they support, such as 'compareVersion'. ```typescript const parseVersion = () => {} ... export const compareVersion = () => { const v = parseVersion() ... ``` -------------------------------- ### Test Router Interaction with Basic Mock Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md Illustrates a common testing scenario where a Router service is mocked to verify navigation after an action. This example highlights potential issues with tightly coupled tests that depend on specific method implementations. ```tsx it('Successful book creation redirects to books list page', async () => { const fakeBookFormValues = makeFakeBookFormValues() const routerMock = mock() const sut = new CreateBookScreenStore(routerMock) await sut.createBook(fakeBookFormValues) expect(routerMock.push).toBeCalledWith('/books') }) ``` -------------------------------- ### Implement TariffRepository Source: https://github.com/redstack-dev/docs/blob/main/architecture/data.en.md Implements the TariffRepository class, which depends on TariffsNetworkSources. It provides a method to get tariffs by proxying the call to the injected network source. ```tsx export class TariffRepository { constructor(private readonly tariffNetworkSources: TariffsNetworkSources) { this.tariffNetworkSources = tariffNetworkSources } public getTariffs = async () => this.tariffNetworkSources.getTariffs() } ``` -------------------------------- ### Invalid Plural Noun Naming (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples of poorly named functions dealing with plural nouns, where the distinction between singular and plural is unclear. ```TypeScript class Example { public getDraft = () => {} public getDrafts = () => {} public sendDraft = () => {} public sendDrafts = () => {} public calcDraftSum = () => {} public calcDraftsSum = () => {} } ``` -------------------------------- ### Domain Services: Cart Calculator Example Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/domain.en.md Demonstrates a service class within the domain layer responsible for calculating cart totals and discounts. It takes an array of CartItem objects and returns numerical results. ```ts // domain/services/cart-calculator.ts export class CartCalculator { public calculateTotal(items: CartItem[]): number { return items.reduce((total, item) => total + (item.price * item.quantity), 0) } public calculateDiscount(total: number, discountPercent: number): number { return total * (discountPercent / 100) } } ``` -------------------------------- ### Shared Layer Structure Example (React) Source: https://github.com/redstack-dev/docs/blob/main/architecture/shared.en.md Illustrates a typical directory structure for the Shared layer in a React-based project, showcasing common subdirectories like constants, types, utils, services, stores, and UI components. ```tree ├── app/ ├── screens/ ├── modules/ ├── data/ └── shared/ | ├── constants/ | ├── types/ | ├── utils/ | | ├── format-date/ | | ├── external.ts | | └── index.ts | ├── services/ | ├── stores/ | ├── ui/ | └── index.ts ``` -------------------------------- ### Valid Handler Props Naming ('on' Prefix) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/react/props.en.md This snippet shows the correct naming convention for handler props, which should all start with the 'on' prefix. ```ts interface Props { onClick: () => void onChange: () => void onDelete: () => void } ``` -------------------------------- ### Invalid Repository Directory Structure Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/data/repositories.en.md An example of an incorrect repository directory structure where the repository file is directly in the parent directory instead of its own dedicated folder. ```directory-structure ├── repositories/ | ├── user-repository.ts | └── index.ts ``` -------------------------------- ### Component with Boolean Check (tsx) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md A React component example demonstrating the use of a function prefixed with 'check' to avoid naming conflicts with props. ```tsx // Have to rename the function due to conflicts with parameters and variables import { isEmpty as checkIsEmpty } from 'utils' export function UserInfo({ data, isEmpty }: Props) { const isEmptyData = isEmpty && checkIsEmpty(data) return ( {isEmpty && } ) } ``` -------------------------------- ### Domain Stores: Cart Store Example with MobX Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/domain.en.md Illustrates a store for managing cart state, utilizing services for business logic and MobX for state management. It depends on CartRepository for data operations and CartCalculator for calculations. ```ts import { CartRepository } from '@data/repositories/cart-repository' // domain/stores/cart-store/cart-store.ts import { makeAutoObservable } from 'mobx' import { CartCalculator } from '../services/cart-calculator' export class CartStore { private items: CartItem[] = [] private calculator: CartCalculator constructor( private readonly cartRepository: CartRepository ) { this.calculator = new CartCalculator() makeAutoObservable(this) } public get total(): number { return this.calculator.calculateTotal(this.items) } public addItem(item: CartItem) { this.items.push(item) } public removeItem(itemId: string) { this.items = this.items.filter(item => item.id !== itemId) } } ``` -------------------------------- ### Shared Layer Structure Example (with external.ts) Source: https://github.com/redstack-dev/docs/blob/main/architecture/shared.en.md Demonstrates the Shared layer structure, emphasizing the role of `external.ts` in managing and re-exporting external dependencies for controlled library interaction and easier migration. ```tree ├── app/ ├── screens/ ├── modules/ ├── data/ └── shared/ | ├── constants/ | ├── types/ | ├── utils/ | | ├── format-date/ | | ├── external.ts | | └── index.ts | ├── services/ | ├── stores/ | ├── ui/ | └── index.ts ``` -------------------------------- ### Valid vs. Invalid Naming for Lifecycle Methods Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/features/ui-store.en.md Provides examples of valid and invalid naming conventions for `UIStore` methods that are tied to component lifecycle events. Emphasizes using clear names like `mount` and `unmount` for unambiguous connections. ```tsx import { useLocalObservable } from 'mobx-react-lite'; import { createUIStore } from './ui-store'; const List = () => { const containerRef = useRef(); const { mount, unmount } = useLocalObservable(createUIStore); useEffect(() => { mount(containerRef); return unmount; }, []); ... }; ``` ```tsx import { useLocalObservable } from 'mobx-react-lite'; import { createUIStore } from './ui-store'; const List = () => { const containerRef = useRef(); const { init, destroy } = useLocalObservable(createUIStore); useEffect(() => { init(containerRef); return destroy; }, []); ... ``` -------------------------------- ### Invalid: Circular Dependency Import (TSX) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/constants.en.md Shows the `header.tsx` file in an invalid setup, attempting to import `DEFAULT_NAME` from `../info`, which creates a circular dependency with `info.tsx`. ```tsx // Circular dependency with Info.tsx import { DEFAULT_NAME } from '../info' function Header() { return (
{DEFAULT_NAME}
) } ``` -------------------------------- ### Invalid Props Naming (Capitalized Start) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/react/props.en.md Shows the incorrect way of naming props, where some props start with an uppercase letter. ```ts interface Props { title: string userName: string onClick: () => void Footer: ReactNode Header: FunctionComponent } ``` -------------------------------- ### Invalid Plural Noun Naming (tsx) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/naming/functions.en.md Examples of component names that are ambiguous regarding singular vs. plural entities, making them difficult to differentiate. ```tsx function DraftScreen() {} function DraftsScreen() {} ``` -------------------------------- ### Incorrect Unit Test: Mocking HTTP Endpoint for GoodsListStore (JavaScript) Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md Another example of an incorrect unit test for `GoodsListStore.getList`. This test uses `setupServer` to mock the HTTP endpoint directly. Similar to mocking `axios`, this approach tightly couples the test to the network endpoint and the structure of the backend response, reducing refactoring resistance. ```JavaScript it('Book list is formatted for display', async () => { const fakeBookList = bookRepositoryFaker.makeBookList(2, { price: 1000 }) const fakeBookListItem = fakeBookList.data[0] const mockServer = setupServer([ http.get('https://rest-endpoint.example/path/to/books', () => { return HttpResponse.json(fakeBookList) }), ]) mockServer.listen({ onUnhandledRequest: 'error' }) const bookRepository = new BookRepository(bookNetworkSources) const sut = new GoodsListStore(bookRepository) const resultList = await sut.getList() expect(resultList[0]).toMatchObject({ id: fakeBookListItem.id, name: fakeBookListItem.name, price: '1 000 руб.', }) }) ``` -------------------------------- ### Correct Unit Test: Mocking BookRepository for GoodsListStore (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md A correct unit test example for `GoodsListStore.getList`. This test mocks the `BookRepository` dependency directly using a mocking utility. This approach isolates the `GoodsListStore` from the `BookRepository`'s implementation details, including its network interactions, leading to better refactoring resistance. ```TypeScript describe('GoodsListStore', () => { it('Book list is formatted for display', async () => { const fakeBookList = bookRepositoryFaker.makeBookList(2, { price: 1000 }) const fakeBookListItem = fakeBookList.data[0] const bookRepositoryMock = mock({ getBookList: async () => fakeBookList, }) const sut = new GoodsListStore(bookRepositoryMock) const resultList = await sut.getList() expect(resultList[0]).toMatchObject({ id: fakeBookListItem.id, name: fakeBookListItem.name, price: '1 000 руб.', }) }) }) ``` -------------------------------- ### DI Example for React Component AddToCartButton Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/mock-and-stub.en.md Shows Dependency Injection for a React component AddToCartButton. The component receives its store dependency via props, depending on an abstract interface for improved testability. ```tsx interface AddToCartButtonProps { store: ProductCartManagerStore } const AddToCartButton = observer(({ store }: AddToCartButtonProps) => { const { addToCart } = store return }) ``` -------------------------------- ### Valid AAA Pattern in Tests Source: https://github.com/redstack-dev/docs/blob/main/style-guides/testing/structure.en.md Provides examples of tests structured according to the Arrange-Act-Assert (AAA) pattern, emphasizing the correct sequence of operations without unnecessary comments for each block. ```ts it('Item list and counter reset after reset', () => { const cartSourcesStub = mock() const sut = new CartRepository(cartSourcesStub, createCacheService()) const goodsCountQuery = sut.getGoodsCountQuery() const goodsQuery = sut.getGoodsQuery() sut.resetCartCache() expect(goodsCountQuery.data).toBe(0) expect(goodsQuery.data).toEqual([]) }) ``` ```tsx it('OnChange is called without passed isExpanded', async () => { const onChangeSpy = vi.fn() renderWithTheme( } onChange={onChangeSpy} > Content , ) const title = screen.getByRole('button', { name: 'Test' }) await userEvents.click(title) expect(onChangeSpy).toHaveBeenCalled() }) ``` -------------------------------- ### Invalid: Local Constant Naming (TS) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/constants.en.md Highlights an invalid example where a constant outside block scope is not in UPPER_CASE, and also shows the correct usage of a local constant. ```ts // Constant outside block scope const defaultFactor = 2 function calc(a: number, b: number, factor: number = defaultFactor) { // Constant inside block scope const summ = a + b return summ * factor } ``` -------------------------------- ### React Component Using UIStore Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/modules/features/ui-store.ru.md Demonstrates how a React component can utilize a UIStore, specifically showing the correct usage of methods that do not have React-specific prefixes. ```tsx import { useLocalObservable } from 'mobx-react-lite' import { createUIStore } from './ui-store' function DeleteItem() { const { deleteItem } = useLocalObservable(createUIStore) return ( ) } ``` -------------------------------- ### Invalid Domain Validations Naming Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/modules/validations.en.md Provides an example of incorrect naming for validation files within the 'domain/validations' directory, highlighting the use of 'validation' prefixes and postfixes. ```typescript ├── domain/ | ├── validations/ | | |── user-validation-schema/ | | |── text-field-validation-rule/ | | └── index.ts | └── index.ts ``` -------------------------------- ### Invalid DTO Naming: Redundant Postfix Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/data/dto.en.md An invalid example showing a redundant 'DTO' postfix in the 'CreationUserInputDTO' interface. The convention prefers only the 'Input' postfix for clarity. ```ts // DTO postfix is redundant when using modular approach export interface CreationUserInputDTO { id: string } ``` -------------------------------- ### Example Shared Layer Structure (React) Source: https://github.com/redstack-dev/docs/blob/main/architecture/overview.en.md Details the typical structure of the 'Shared' layer in a React-based Redstack project. This layer contains reusable, domain-agnostic code, including constants, types, utilities, services, stores, and UI components. ```tree ├── app/ ├── screens/ ├── modules/ ├── data/ └── shared/ ├── constants/ ├── types/ ├── utils/ ├── services/ ├── stores/ ├── ui/ | ├── components/ | ├── hooks/ | ├── external.ts | └── index.ts └── index.ts ``` -------------------------------- ### Invalid DTO Export: Namespace Usage Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/data/dto.en.md An invalid example demonstrating the use of a namespace for exporting DTOs instead of the preferred 'export * as' syntax. This approach is considered less maintainable. ```ts // Using namespace instead of export * as export namespace UserNetworkSourcesDTO { export type CreationUserInput = {...}; } ``` -------------------------------- ### Formatting Props for Components (Valid) Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/features/ui-logic.en.md This valid example shows formatting props for child components using a UIStore. The logic for preparing props is externalized, allowing the component to receive pre-formatted props. ```tsx import { useLocalObservable } from 'mobx-react-lite' import { createUIStore } from './ui-store' export function Card() { const { viewerTitle, descriptions } = useLocalObservable(createUIStore) return ( ) } ``` -------------------------------- ### Format Date for Display (Valid) Source: https://github.com/redstack-dev/docs/blob/main/architecture/modules/features/ui-logic.en.md This valid example demonstrates formatting dates for display by utilizing a UIStore. The date formatting logic is handled externally, keeping the component focused on rendering. ```tsx import { useLocalObservable } from 'mobx-react-lite' import { createUIStore } from './ui-store' export function Card(props: Props) { const { issueDate } = useLocalObservable(() => createUIStore(props)) return ( {issueDate} ) } ``` -------------------------------- ### Avoid Leaking SUT Implementation Details in Tests (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/unit-testing/antipatterns.en.md This section addresses the antipattern of duplicating SUT logic within tests. The invalid examples show tests that reimplement formatting logic. The valid examples demonstrate how to test by providing explicit expected outputs, ensuring tests are robust and independent of implementation details. ```tsx describe('formatPriceToView', () => { it.each([[1000], [10000], [100000], [1000000]])( 'For "%s" non-breaking space is added in numbers', (input) => { const result = formatPriceToView(input); expect(result).toBe(`${input.toLocaleString('ru')} руб.`); }, ); }; ``` ```tsx describe('formatPriceToView', () => { it.each([ [1000, '1 000 руб.'], [10000, '10 000 руб.'], [100000, '100 000 руб.'], [1000000, '1 000 000 руб.'], ])('For "%s" non-breaking space is added in numbers', (input, output) => { const result = formatPriceToView(input); expect(result).toBe(output); }); }; ``` ```tsx describe('formatBookItem', () => { it('Book data is returned in display format', () => { const fakeBookItem = bookRepositoryFaker.makeBookItem({ price: 1000 }) const result = formatBookItemToView(fakeBookItem) expect(result).toEqual({ ...fakeBookItem, price: '1 000 руб.', }) }) }) ``` ```tsx describe('formatBookItem', () => { it('Book data is returned in display format', () => { const fakeBookItem = bookRepositoryFaker.makeBookItem({ price: 1000 }) const result = formatBookItemToView(fakeBookItem) expect(result).toEqual({ id: fakeBookItem.id, name: fakeBookItem.name, price: '1 000 руб.', }) }) }) ``` -------------------------------- ### Invalid CamelCase Directory Naming (TypeScript) Source: https://github.com/redstack-dev/docs/blob/main/style-guides/architecture/data/sources.en.md Shows an invalid directory naming convention that uses camelCase instead of the required kebab-case, violating the project's style guide. ```typescript ├── sources/ | ├── userNetworkSources/ | └── index.ts ``` -------------------------------- ### Define UserRepository with Caching Source: https://github.com/redstack-dev/docs/blob/main/architecture/data.en.md Example of a UserRepository class that utilizes a CacheService for managing data queries. It defines query keys and uses mobx-tanstack-query for caching operations, abstracting network source interactions. ```tsx import { CacheService } from '@shared' import { UserNetworkSources } from '../../sources' export class UserRepository { private readonly keys = { contactInfoCacheKey: 'contact-info-cache-key' } constructor( private readonly userNetworkSources: UserNetworkSources, private readonly cache: CacheService, ) {} public getContactInfoQuery = () => this.cache.createQuery( async () => { const { data } = this.userNetworkSources.getContactInfo() return data }, { queryKey: [this.keys.contactInfoCacheKey] } ) } ```