Skip to content

Feature: Traits and Presets System #42

@Goldziher

Description

@Goldziher

Feature: Variants and States System

Add support for predefined factory variations that allow users to define different states or variants of a factory and apply them selectively.

Motivation

When testing, it's common to need variations of the same entity - for example, an admin user vs regular user, a published post vs draft post, or a deleted record vs active record. This feature would provide a clean API for defining and using these variations.

Proposed Features

  • Variant registration and application system

    • Register named variants with specific overrides
    • Apply single or multiple variants when building
  • State management

    • Define common states (active, deleted, archived, etc.)
    • Support for state transitions
  • Support for both static and dynamic variants

    • Static: Fixed property overrides
    • Dynamic: Functions that compute overrides
  • Handle variant conflicts appropriately

    • Clear precedence rules when multiple variants modify same fields
    • Option to merge or replace conflicting values
  • Multiple variants can be applied simultaneously

    • Compose variants together
    • Control order of application

Implementation Requirements

  • TypeScript discriminated union support for type-safe variants
  • Clear inheritance behavior for composed factories
  • Efficient variant storage and lookup
  • Support for async variant functions
  • Integration with existing extend() and compose() methods

Example API

const UserFactory = new Factory<User>((faker) => ({
  id: faker.string.uuid(),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  role: 'user',
  status: 'active',
  isVerified: false
}))
.variant('admin', {
  role: 'admin',
  permissions: ['read', 'write', 'delete']
})
.variant('verified', {
  isVerified: true,
  verifiedAt: new Date()
})
.variant('deleted', (faker) => ({
  status: 'deleted',
  deletedAt: faker.date.recent()
}));

// Usage
const adminUser = UserFactory.build({ variant: 'admin' });
const verifiedAdmin = UserFactory.build({ variants: ['admin', 'verified'] });
const deletedUser = UserFactory.build({ variant: 'deleted' });

// With states
const PostFactory = new Factory<Post>((faker) => ({
  id: faker.string.uuid(),
  title: faker.lorem.sentence(),
  status: 'draft'
}))
.state('published', {
  status: 'published',
  publishedAt: new Date()
})
.state('archived', {
  status: 'archived',
  archivedAt: new Date()
});

// State transitions
const post = PostFactory.build({ state: 'draft' });
const publishedPost = PostFactory.transition(post, 'published');

TypeScript Support

// Discriminated unions
type User = BaseUser | AdminUser | DeletedUser;

const factory = new Factory<User>()
  .variant<AdminUser>('admin', { type: 'admin', ... })
  .variant<DeletedUser>('deleted', { type: 'deleted', ... });

// Type inference
const admin = factory.build({ variant: 'admin' }); // Type: AdminUser

Testing Requirements

  • Unit tests for variant registration and application
  • Tests for variant conflict resolution
  • Type safety tests with discriminated unions
  • Performance tests with many variants
  • Integration tests with other factory methods

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgood first issueGood for newcomershelp wantedExtra attention is neededonlydust-waveContribute to awesome OSS repos during OnlyDust's open source week

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions