framework

package
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package framework provides a unified framework definition system for test framework detection and parsing. It replaces the dual registry system (matchers + strategies) with a single unified Definition type.

Example (Definition)

ExampleDefinition demonstrates how to create a complete framework definition.

package main

import (
	"fmt"

	"github.com/specvital/core/pkg/domain"
	"github.com/specvital/core/pkg/parser/framework"
	"github.com/specvital/core/pkg/parser/framework/matchers"
)

func main() {
	// Create a framework definition for a hypothetical test framework
	def := &framework.Definition{
		Name: "mytest",
		Languages: []domain.Language{
			domain.LanguageTypeScript,
			domain.LanguageJavaScript,
		},
		Matchers: []framework.Matcher{
			// Match import statements
			matchers.NewImportMatcher("mytest", "mytest/"),

			// Match config files
			matchers.NewConfigMatcher("mytest.config.js", "mytest.config.ts"),

			// Match content patterns (optional)
			matchers.NewContentMatcherFromStrings(
				`\bmytest\s*\(`,
				`\bmytest\.describe\s*\(`,
			),
		},
		Priority: framework.PriorityGeneric,
	}

	// Register the framework
	registry := framework.NewRegistry()
	registry.Register(def)

	// Find the framework by name
	found := registry.Find("mytest")
	fmt.Printf("Framework: %s\n", found.Name)
	fmt.Printf("Priority: %d\n", found.Priority)
	fmt.Printf("Languages: %v\n", found.Languages)
	fmt.Printf("Matchers: %d\n", len(found.Matchers))

}
Output:

Framework: mytest
Priority: 100
Languages: [typescript javascript]
Matchers: 3
Example (Matcher)

ExampleMatcher demonstrates how to use matchers for framework detection.

package main

import (
	"context"
	"fmt"

	"github.com/specvital/core/pkg/parser/framework"
	"github.com/specvital/core/pkg/parser/framework/matchers"
)

func main() {
	// Create matchers
	importMatcher := matchers.NewImportMatcher("vitest", "vitest/")
	configMatcher := matchers.NewConfigMatcher("vitest.config.ts")

	ctx := context.Background()

	// Test import signal
	importSignal := framework.Signal{
		Type:  framework.SignalImport,
		Value: "vitest/config",
	}
	result := importMatcher.Match(ctx, importSignal)
	fmt.Printf("Import match confidence: %d\n", result.Confidence)

	// Test config signal
	configSignal := framework.Signal{
		Type:  framework.SignalConfigFile,
		Value: "/project/vitest.config.ts",
	}
	result = configMatcher.Match(ctx, configSignal)
	fmt.Printf("Config match confidence: %d\n", result.Confidence)

}
Output:

Import match confidence: 100
Config match confidence: 100
Example (ProjectScope)

ExampleProjectScope demonstrates project-wide configuration management.

package main

import (
	"fmt"

	"github.com/specvital/core/pkg/parser/framework"
)

func main() {
	// Create a project scope
	scope := framework.NewProjectScope()

	// Add framework configurations
	scope.AddConfig("jest.config.js", &framework.ConfigScope{
		Framework:   "jest",
		ConfigPath:  "jest.config.js",
		GlobalsMode: true,
		TestPatterns: []string{
			"**/*.test.js",
			"**/*.spec.js",
		},
		ExcludePatterns: []string{
			"**/node_modules/**",
		},
	})

	scope.AddConfig("apps/web/vitest.config.ts", &framework.ConfigScope{
		Framework:   "vitest",
		ConfigPath:  "apps/web/vitest.config.ts",
		GlobalsMode: false,
		TestPatterns: []string{
			"**/*.test.ts",
		},
	})

	// Find configuration
	jestConfig := scope.FindConfig("jest.config.js")
	fmt.Printf("Jest config globals mode: %t\n", jestConfig.GlobalsMode)

	// Check if config exists
	hasVitest := scope.HasConfigFile("apps/web/vitest.config.ts")
	fmt.Printf("Has Vitest config: %t\n", hasVitest)

}
Output:

Jest config globals mode: true
Has Vitest config: true
Example (Registry)

ExampleRegistry demonstrates registry operations.

package main

import (
	"fmt"

	"github.com/specvital/core/pkg/domain"
	"github.com/specvital/core/pkg/parser/framework"
)

func main() {
	// Create a new registry
	reg := framework.NewRegistry()

	// Register multiple frameworks
	reg.Register(&framework.Definition{
		Name:      "jest",
		Languages: []domain.Language{domain.LanguageTypeScript},
		Priority:  framework.PriorityGeneric,
	})

	reg.Register(&framework.Definition{
		Name:      "vitest",
		Languages: []domain.Language{domain.LanguageTypeScript},
		Priority:  framework.PrioritySpecialized,
	})

	reg.Register(&framework.Definition{
		Name:      "playwright",
		Languages: []domain.Language{domain.LanguageTypeScript},
		Priority:  framework.PriorityE2E,
	})

	// Get all frameworks (sorted by priority)
	all := reg.All()
	fmt.Println("Frameworks by priority:")
	for _, def := range all {
		fmt.Printf("  %s (priority: %d)\n", def.Name, def.Priority)
	}

	// Find by language
	tsFrameworks := reg.FindByLanguage(domain.LanguageTypeScript)
	fmt.Printf("\nTypeScript frameworks: %d\n", len(tsFrameworks))

	// Find by name
	jest := reg.Find("jest")
	fmt.Printf("Found framework: %s\n", jest.Name)

}
Output:

Frameworks by priority:
  vitest (priority: 200)
  playwright (priority: 150)
  jest (priority: 100)

TypeScript frameworks: 3
Found framework: jest

Index

Examples

Constants

View Source
const (
	// PriorityGeneric is for common, general-purpose test frameworks.
	// These frameworks typically support a wide range of test files and patterns.
	// Examples: Jest, Go testing
	PriorityGeneric = 100

	// PriorityE2E is for end-to-end testing frameworks.
	// These frameworks are more specialized and should be checked before generic frameworks.
	// Examples: Playwright, Cypress
	PriorityE2E = 150

	// PrioritySpecialized is for highly specialized frameworks with unique characteristics.
	// These should be checked first to avoid false matches with generic frameworks.
	// Examples: Vitest with globals mode (requires explicit import detection)
	PrioritySpecialized = 200
)

Priority constants determine the order in which frameworks are checked during detection. Higher priority frameworks are evaluated first.

Use increments of 50 to allow for future insertions between priority levels.

View Source
const (
	FrameworkCargoTest    = "cargo-test"
	FrameworkCypress      = "cypress"
	FrameworkGoTesting    = "go-testing"
	FrameworkGTest        = "gtest"
	FrameworkJest         = "jest"
	FrameworkJUnit4       = "junit4"
	FrameworkJUnit5       = "junit5"
	FrameworkKotest       = "kotest"
	FrameworkMinitest     = "minitest"
	FrameworkMocha        = "mocha"
	FrameworkMSTest       = "mstest"
	FrameworkNUnit        = "nunit"
	FrameworkPHPUnit      = "phpunit"
	FrameworkPlaywright   = "playwright"
	FrameworkPytest       = "pytest"
	FrameworkRSpec        = "rspec"
	FrameworkSwiftTesting = "swift-testing"
	FrameworkTestNG       = "testng"
	FrameworkUnittest     = "unittest"
	FrameworkVitest       = "vitest"
	FrameworkXCTest       = "xctest"
	FrameworkXUnit        = "xunit"
)

Common framework names as constants to ensure consistency.

Variables

This section is empty.

Functions

func Clear

func Clear()

Clear removes all frameworks (useful for testing).

func Names

func Names() []string

func Register

func Register(def *Definition)

Register adds a framework definition to the default registry. Typically called from framework package init() functions.

Types

type AggregatedProjectScope

type AggregatedProjectScope struct {
	Configs     map[string]*ConfigScope
	ConfigFiles []string
}

AggregatedProjectScope aggregates multiple ConfigScope instances for hierarchical config resolution.

func NewProjectScope

func NewProjectScope() *AggregatedProjectScope

func (*AggregatedProjectScope) AddConfig

func (ps *AggregatedProjectScope) AddConfig(path string, scope *ConfigScope)

func (*AggregatedProjectScope) FindConfig

func (ps *AggregatedProjectScope) FindConfig(path string) *ConfigScope

func (*AggregatedProjectScope) HasConfigFile

func (ps *AggregatedProjectScope) HasConfigFile(filename string) bool

type ConfigParser

type ConfigParser interface {
	// Parse reads and interprets a framework configuration file.
	// Returns ConfigScope containing parsed settings like test patterns, globals mode, etc.
	// Returns error if the config file cannot be parsed.
	Parse(ctx context.Context, configPath string, content []byte) (*ConfigScope, error)
}

ConfigParser extracts configuration information from framework config files.

type ConfigScope

type ConfigScope struct {
	ConfigPath      string
	BaseDir         string
	Include         []string
	Exclude         []string
	Settings        map[string]interface{}
	Projects        []ProjectScope
	Framework       string
	TestPatterns    []string
	ExcludePatterns []string
	RootDir         string

	// Roots contains additional root directories (e.g., Jest's roots config).
	// When set, Contains() checks if a file is within any of these roots.
	Roots []string

	// GlobalsMode: when true, test files don't need explicit imports (e.g., Jest default).
	GlobalsMode bool
}

ConfigScope defines the effective scope of a config file. Handles root resolution, include/exclude patterns, and workspace projects.

func NewConfigScope

func NewConfigScope(configPath string, root string) *ConfigScope

NewConfigScope creates a ConfigScope with root resolved relative to config directory.

func (*ConfigScope) Contains

func (s *ConfigScope) Contains(filePath string) bool

Contains checks if filePath is within this config's scope.

func (*ConfigScope) Depth

func (s *ConfigScope) Depth() int

Depth returns the directory depth of BaseDir (used for selecting nearest config).

func (*ConfigScope) FindMatchingProject

func (s *ConfigScope) FindMatchingProject(filePath string) *ProjectScope

FindMatchingProject finds the most specific project for a file.

type Definition

type Definition struct {
	// Name is the framework identifier (e.g., "jest", "vitest", "playwright").
	Name string

	// Languages specifies which programming languages this framework supports.
	Languages []domain.Language

	// Matchers are detection rules that determine if a test file uses this framework.
	// Multiple matchers can be registered for different detection strategies
	// (import statements, config files, file content patterns, etc.).
	Matchers []Matcher

	// ConfigParser extracts configuration information from framework config files.
	// Used to determine settings like globals mode, test patterns, etc.
	// May be nil if the framework doesn't have configuration files.
	ConfigParser ConfigParser

	// Parser extracts test definitions from source code files.
	// Produces a domain.TestFile with all test suites and test cases.
	Parser Parser

	// Priority determines detection order when multiple frameworks could match.
	// Higher priority frameworks are checked first.
	// Use PriorityGeneric (100), PriorityE2E (150), or PrioritySpecialized (200).
	Priority int
}

Definition represents a unified test framework specification that combines: - Framework identity (Name, Languages) - Detection components (Matchers) - Configuration parsing (ConfigParser) - Test file parsing (Parser) - Priority for detection ordering

Each framework (Jest, Vitest, Playwright, Go testing) provides a Definition that is registered with the global Registry.

func All

func All() []*Definition

All returns all registered frameworks (sorted by priority).

func Find

func Find(name string) *Definition

func FindByLanguage

func FindByLanguage(lang domain.Language) []*Definition

FindByLanguage returns frameworks supporting the language (sorted by priority).

type MatchResult

type MatchResult struct {
	// Confidence is a score from 0-100 indicating how certain the match is.
	// 0 = no match, 100 = definite match.
	Confidence int

	// Evidence is a list of specific indicators that support this match.
	// For example: ["import '@jest/globals'", "jest.config.js found"].
	Evidence []string

	// Negative indicates this is a definite non-match.
	// If true, this framework should be excluded from consideration.
	// For example, Vitest with globals:false should not match files without imports.
	Negative bool
}

MatchResult contains the outcome of a matcher evaluation.

func DefiniteMatch

func DefiniteMatch(evidence ...string) MatchResult

DefiniteMatch returns a MatchResult indicating a definite match with evidence.

func NegativeMatch

func NegativeMatch(evidence ...string) MatchResult

NegativeMatch returns a MatchResult indicating this framework definitely does not match.

func NoMatch

func NoMatch() MatchResult

NoMatch returns a MatchResult indicating no match was found.

func PartialMatch

func PartialMatch(confidence int, evidence ...string) MatchResult

PartialMatch returns a MatchResult with a specific confidence level and evidence.

type Matcher

type Matcher interface {
	// Match evaluates a signal and returns a confidence score.
	// Returns MatchResult with confidence (0-100) and supporting evidence.
	Match(ctx context.Context, signal Signal) MatchResult
}

Matcher defines the interface for framework detection rules. Matchers analyze different signals (imports, config files, content patterns) to determine if a test file belongs to a specific framework.

type Parser

type Parser interface {
	// Parse analyzes source code and extracts test suites and test cases.
	// Returns a domain.TestFile containing all discovered tests.
	// Returns error if the file cannot be parsed or doesn't contain valid tests.
	Parse(ctx context.Context, source []byte, filename string) (*domain.TestFile, error)
}

Parser extracts test definitions from source code files.

type ProjectScope

type ProjectScope struct {
	Name     string
	BaseDir  string
	Include  []string
	Exclude  []string
	Settings map[string]interface{}
}

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry manages registered framework definitions (thread-safe).

func DefaultRegistry

func DefaultRegistry() *Registry

func NewRegistry

func NewRegistry() *Registry

func (*Registry) All

func (r *Registry) All() []*Definition

func (*Registry) Clear

func (r *Registry) Clear()

func (*Registry) Find

func (r *Registry) Find(name string) *Definition

func (*Registry) FindByLanguage

func (r *Registry) FindByLanguage(lang domain.Language) []*Definition

func (*Registry) Names

func (r *Registry) Names() []string

func (*Registry) Register

func (r *Registry) Register(def *Definition)

type Signal

type Signal struct {
	// Type indicates what kind of signal this is (import, config file, etc.).
	Type SignalType

	// Value contains the signal data (import path, file name, content, etc.).
	Value string

	// Context provides additional metadata specific to the signal type.
	// For example, file content for content-based matching.
	Context interface{}
}

Signal represents a detection signal that matchers can evaluate.

type SignalType

type SignalType int

SignalType categorizes different kinds of detection signals.

const (
	// SignalImport represents an import statement (e.g., "import { test } from 'vitest'").
	SignalImport SignalType = iota

	// SignalConfigFile represents a config file name (e.g., "jest.config.js").
	SignalConfigFile

	// SignalFileContent represents file content patterns (e.g., "test.describe(").
	SignalFileContent

	// SignalFileName represents a test file name pattern (e.g., "*.test.ts").
	SignalFileName
)

Directories

Path Synopsis
Package matchers provides reusable matcher implementations for framework detection.
Package matchers provides reusable matcher implementations for framework detection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL