or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

code-organization-rules.mdcode-style-rules.mdexpectation-rules.mdindex.mdjasmine-matcher-rules.mdpromise-rules.mdspy-rules.mdtest-structure-rules.md
tile.json

code-organization-rules.mddocs/

Code Organization Rules

Rules that enforce proper code organization within test suites to maintain clean, maintainable, and reliable test code.

Capabilities

no-describe-variables

Prevents variable declarations within describe blocks to maintain proper test isolation and avoid shared mutable state.

/**
 * Rule: no-describe-variables
 * Disallows variable declarations in describe blocks
 * @config [0|1|2] - Rule severity level
 */
'jasmine/no-describe-variables': [0|1|2]

Examples:

// ❌ Bad - variables in describe block
describe('User service', function() {
  let user = createUser(); // Shared mutable state
  let config = { timeout: 5000 }; // Could cause test interdependencies
  
  it('should validate user', function() {
    user.name = 'Modified'; // Affects other tests
    expect(validateUser(user)).toBe(true);
  });
});

// ✅ Good - variables in beforeEach
describe('User service', function() {
  let user, config;
  
  beforeEach(function() {
    user = createUser(); // Fresh instance for each test
    config = { timeout: 5000 }; // Isolated configuration
  });
  
  it('should validate user', function() {
    user.name = 'Modified'; // Won't affect other tests
    expect(validateUser(user)).toBe(true);
  });
});

no-suite-callback-args

Prevents the use of callback arguments (like done) in suite callbacks (describe), which is invalid and can cause test failures.

/**
 * Rule: no-suite-callback-args
 * Disallows callback arguments in describe blocks
 * @config [0|1|2] - Rule severity level
 */
'jasmine/no-suite-callback-args': [0|1|2]

Examples:

// ❌ Bad - done callback in describe
describe('Async suite', function(done) { // Invalid - describe doesn't support done
  // This will cause test failures
});

// ❌ Bad - any callback args in describe
describe('Suite with args', function(callback, next) { // Invalid
  // describe blocks don't accept callback parameters
});

// ✅ Good - no arguments in describe
describe('Async suite', function() {
  // Use beforeEach/afterEach for async setup
  beforeEach(function(done) {
    setupAsync(done);
  });
  
  it('should handle async test', function(done) {
    performAsyncAction(done);
  });
});

no-global-setup

Prevents global setup and teardown code that can cause test interdependencies and make tests harder to debug.

/**
 * Rule: no-global-setup
 * Disallows global setup/teardown outside describe blocks
 * @config [0|1|2] - Rule severity level
 */
'jasmine/no-global-setup': [0|1|2]

Examples:

// ❌ Bad - global setup
beforeEach(function() { // Global beforeEach affects all tests
  globalSetup();
});

afterEach(function() { // Global afterEach affects all tests
  globalCleanup();
});

describe('Some tests', function() {
  it('test 1', function() { /* ... */ });
});

// ✅ Good - scoped setup
describe('Feature tests', function() {
  beforeEach(function() { // Scoped to this describe block
    featureSetup();
  });
  
  afterEach(function() { // Scoped to this describe block
    featureCleanup();
  });
  
  it('should work correctly', function() { /* ... */ });
});

no-expect-in-setup-teardown

Prevents expectation calls in setup and teardown methods to keep test structure clean and ensure proper test isolation.

/**
 * Rule: no-expect-in-setup-teardown
 * Disallows expect() calls in beforeEach/afterEach blocks
 * @config [0|1|2, ...expectationFunctions] - Rule severity and custom expectation function names
 * @default expectationFunctions ['expect()', 'expectAsync()']
 */
'jasmine/no-expect-in-setup-teardown': [0|1|2, 'expect()', 'expectAsync()']

Configuration Examples:

// Default configuration
'jasmine/no-expect-in-setup-teardown': 1

// Custom expectation functions
'jasmine/no-expect-in-setup-teardown': [1, 'expect()', 'expectAsync()', 'customExpect()']

Examples:

// ❌ Bad - expectations in setup/teardown
describe('Service tests', function() {
  beforeEach(function() {
    const service = createService();
    expect(service).toBeDefined(); // Should not test in setup
  });
  
  afterEach(function() {
    cleanupService();
    expect(cleanupComplete()).toBe(true); // Should not test in cleanup
  });
});

// ✅ Good - setup without expectations
describe('Service tests', function() {
  let service;
  
  beforeEach(function() {
    service = createService(); // Pure setup
  });
  
  afterEach(function() {
    cleanupService(); // Pure cleanup
  });
  
  it('should create service correctly', function() {
    expect(service).toBeDefined(); // Test in actual spec
    expect(service.isReady()).toBe(true);
  });
  
  it('should clean up properly', function() {
    service.performAction();
    // Test cleanup effects in separate spec if needed
  });
});

Rule Configuration

All code organization rules support standard ESLint severity levels:

  • 0 or "off" - Disable the rule
  • 1 or "warn" - Enable as warning
  • 2 or "error" - Enable as error (fails lint)

Recommended Settings:

rules:
  jasmine/no-describe-variables: 0         # Optional - some patterns are valid
  jasmine/no-suite-callback-args: 2        # Error - invalid Jasmine syntax
  jasmine/no-global-setup: 2               # Error - causes test interdependencies
  jasmine/no-expect-in-setup-teardown: 1   # Warning - keeps tests clean

Best Practices

Proper Test Organization

describe('UserService', function() {
  let userService, mockDatabase, testUsers;
  
  beforeEach(function() {
    // Pure setup - no expectations
    mockDatabase = jasmine.createSpyObj('database', ['save', 'find', 'delete']);
    userService = new UserService(mockDatabase);
    testUsers = [
      { id: 1, name: 'Alice', email: 'alice@test.com' },
      { id: 2, name: 'Bob', email: 'bob@test.com' }
    ];
  });
  
  afterEach(function() {
    // Pure cleanup - no expectations
    userService = null;
    mockDatabase = null;
    testUsers = null;
  });
  
  describe('save', function() {
    it('should save valid user', function() {
      const user = testUsers[0];
      mockDatabase.save.and.returnValue(user);
      
      const result = userService.save(user);
      
      expect(mockDatabase.save).toHaveBeenCalledWith(user);
      expect(result).toEqual(user);
    });
    
    it('should validate user before saving', function() {
      const invalidUser = { name: '', email: 'invalid' };
      
      expect(function() {
        userService.save(invalidUser);
      }).toThrow();
      
      expect(mockDatabase.save).not.toHaveBeenCalled();
    });
  });
  
  describe('find', function() {
    it('should find user by id', function() {
      const expectedUser = testUsers[0];
      mockDatabase.find.and.returnValue(expectedUser);
      
      const result = userService.find(1);
      
      expect(mockDatabase.find).toHaveBeenCalledWith(1);
      expect(result).toEqual(expectedUser);
    });
  });
});

Avoiding Common Pitfalls

// ❌ Avoid - shared mutable state
describe('Calculator', function() {
  let result = 0; // This can cause test interdependencies
  
  it('should add', function() {
    result = calculator.add(2, 3);
    expect(result).toBe(5);
  });
  
  it('should multiply', function() {
    result = calculator.multiply(result, 2); // Depends on previous test!
    expect(result).toBe(10);
  });
});

// ✅ Good - isolated tests
describe('Calculator', function() {
  it('should add numbers', function() {
    const result = calculator.add(2, 3);
    expect(result).toBe(5);
  });
  
  it('should multiply numbers', function() {
    const result = calculator.multiply(5, 2);
    expect(result).toBe(10);
  });
});