CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sass-true

Unit testing framework for Sass code with JavaScript test runner integration

Pending
Overview
Eval results
Files

javascript-integration.mddocs/

JavaScript Integration

Complete integration with JavaScript/TypeScript test runners, providing automated CSS parsing, test result reporting, and seamless workflow integration with Mocha, Jest, and other test frameworks.

Capabilities

Main Integration Function

Compiles Sass files and runs tests through JavaScript test runners with automated result parsing.

/**
 * Compile Sass tests and run them through JavaScript test runners
 * @param trueOptions - Configuration object with test runner functions and options
 * @param src - Path to Sass file or Sass source string
 * @param sassOptions - Options passed to Sass compiler
 */
function runSass(
  trueOptions: TrueOptions,
  src: string,
  sassOptions?: any
): void;

interface TrueOptions {
  /** Describe function from your test runner (e.g., Mocha's describe) */
  describe: (description: string, fn: () => void) => void;
  /** It function from your test runner (e.g., Mocha's it) */
  it: (description: string, fn: () => void) => void;
  /** Sass compiler instance or package name ('sass', 'sass-embedded') */
  sass?: any | string;
  /** Whether src parameter is a file path or source string */
  sourceType?: 'path' | 'string';
  /** Number of context lines to show in error messages */
  contextLines?: number;
}

Basic Usage Examples:

// Basic Mocha integration
const path = require('node:path');
const sassTrue = require('sass-true');

const sassFile = path.join(__dirname, 'test.scss');
sassTrue.runSass({ describe, it }, sassFile);
// Jest integration
const path = require('node:path');
const sassTrue = require('sass-true');

describe('Sass Tests', () => {
  const sassFile = path.join(__dirname, 'sass/test.scss');
  sassTrue.runSass({ describe, it }, sassFile);
});
// TypeScript with explicit types
import * as sassTrue from 'sass-true';
import * as path from 'node:path';

const sassFile = path.join(__dirname, 'test.scss');
sassTrue.runSass({ describe, it }, sassFile);

Advanced Configuration

const path = require('node:path');
const sassTrue = require('sass-true');

// With custom Sass compiler
sassTrue.runSass(
  { 
    describe, 
    it, 
    sass: 'sass-embedded',  // Specify compiler
    contextLines: 15        // More error context
  },
  path.join(__dirname, 'test.scss'),
  {
    // Sass options
    loadPaths: ['./styles', './node_modules'],
    style: 'expanded',  // Must be 'expanded', not 'compressed'
    sourceMap: false
  }
);

// Using source string instead of file
const sassSource = `
  @use 'true' as *;
  @include test('String source test') {
    @include assert-equal(1 + 1, 2);
  }
`;

sassTrue.runSass(
  { 
    describe, 
    it, 
    sourceType: 'string'  // Indicate source is a string
  },
  sassSource
);

Custom Importers

For projects using custom import patterns (like tilde notation):

const path = require('node:path');
const { pathToFileURL } = require('node:url');
const sassTrue = require('sass-true');

const importers = [
  {
    findFileUrl(url) {
      if (!url.startsWith('~')) {
        return null;
      }
      return new URL(
        pathToFileURL(path.resolve('node_modules', url.substring(1)))
      );
    },
  },
];

const sassFile = path.join(__dirname, 'test.scss');
sassTrue.runSass({ describe, it }, sassFile, { importers });

Test Result Types

Core Data Structures

The JavaScript integration parses CSS output into structured test results:

interface Assertion {
  /** Description of what is being asserted */
  description: string;
  /** Type of assertion (equal, output, contains, etc.) */
  assertionType?: string;
  /** Actual output from the test */
  output?: string;
  /** Expected output for comparison */
  expected?: string;
  /** Additional details about the assertion */
  details?: string;
  /** Whether the assertion passed */
  passed?: boolean;
  /** Additional assertion-specific properties */
  [key: string]: boolean | string | undefined;
}

interface Test {
  /** Name/description of the test */
  test: string;
  /** Array of assertions within this test */
  assertions: Assertion[];
}

interface Module {
  /** Name/description of the module */
  module: string;
  /** Tests directly within this module */
  tests?: Test[];
  /** Nested sub-modules */
  modules?: Module[];
}

CSS Output Parsing

/**
 * Parse Sass-compiled CSS output containing True test results
 * @param rawCss - CSS string output from Sass compilation
 * @param contextLines - Number of context lines to show in error messages (default: 10)
 * @returns Array of parsed test modules with their results
 */
function parse(rawCss: string, contextLines?: number): Module[];

Usage Example:

const sass = require('sass');
const sassTrue = require('sass-true');
const path = require('node:path');

// Compile Sass file manually
const sassFile = path.join(__dirname, 'test.scss');
const result = sass.compile(sassFile);

// Parse the CSS output for test results
const modules = sassTrue.parse(result.css);

// Process the parsed results
modules.forEach(module => {
  console.log(`Module: ${module.module}`);
  module.tests?.forEach(test => {
    console.log(`  Test: ${test.test}`);
    test.assertions.forEach(assertion => {
      const status = assertion.passed ? 'PASS' : 'FAIL';
      console.log(`    ${status}: ${assertion.description}`);
    });
  });
});

Failure Message Formatting

/**
 * Format detailed failure messages with diffs for failed assertions
 * @param assertion - The failed assertion object
 * @returns Formatted error message with visual diff
 */
function formatFailureMessage(assertion: Assertion): string;

Usage Example:

// This is handled automatically by runSass, but you can use it manually:
const sassTrue = require('sass-true');

const failedAssertion = {
  description: 'Should match expected output',
  assertionType: 'equal',
  output: 'color: red;',
  expected: 'color: blue;',
  passed: false
};

console.log(sassTrue.formatFailureMessage(failedAssertion));
// Outputs formatted diff showing expected vs actual

Framework-Specific Setup

Mocha Integration

// test/sass.test.js
const path = require('node:path');
const sassTrue = require('sass-true');

// Single test file
const sassFile = path.join(__dirname, 'sass/test.scss');
sassTrue.runSass({ describe, it }, sassFile);

// Multiple test files
const testFiles = [
  'sass/utilities.test.scss',  
  'sass/components.test.scss',
  'sass/mixins.test.scss'
];

testFiles.forEach(file => {
  const sassFile = path.join(__dirname, file);
  sassTrue.runSass({ describe, it }, sassFile);
});

Jest Integration

// test/sass.test.js
const path = require('node:path');
const sassTrue = require('sass-true');

describe('Sass Tests', () => {
  const sassFile = path.join(__dirname, 'sass/test.scss');
  sassTrue.runSass({ describe, it }, sassFile);
});

Jest Configuration:

// jest.config.js
module.exports = {
  testEnvironment: 'jest-environment-node-single-context',
  moduleFileExtensions: ['js', 'json', 'scss'],
  // Other Jest config...
};

Other Test Runners

Any test runner with describe and it functions can be used:

// Using AVA (adapted)
const test = require('ava');
const sassTrue = require('sass-true');

// Adapter for AVA
const avaDescribe = (name, fn) => {
  // AVA doesn't have describe, so we just run the function
  fn();
};

const avaIt = (name, fn) => {
  test(name, t => {
    try {
      fn();
      t.pass();
    } catch (error) {
      t.fail(error.message);
    }
  });
};

sassTrue.runSass({ 
  describe: avaDescribe, 
  it: avaIt 
}, './test.scss');

Advanced Usage

Error Handling and Debugging

const sassTrue = require('sass-true');

try {
  sassTrue.runSass({ describe, it }, './test.scss', {
    // More context lines for debugging
    contextLines: 20
  });
} catch (error) {
  console.error('Sass compilation failed:', error.message);
  
  if (error.message.includes('Context')) {
    console.log('Check your Sass syntax and imports');
  }
}

Multiple Sass Compilers

// Test with different Sass implementations
const configs = [
  { sass: 'sass', name: 'Dart Sass' },
  { sass: 'sass-embedded', name: 'Embedded Dart Sass' }
];

configs.forEach(config => {
  describe(`Sass Tests with ${config.name}`, () => {
    sassTrue.runSass(
      { describe, it, sass: config.sass },  
      './test.scss'
    );
  });
});

CSS Output Parsing

The JavaScript integration automatically parses special CSS comments generated by Sass True:

// This Sass test:
@include test('Example test') {
  @include assert-equal(1 + 1, 2);
}

// Generates CSS comments like:
/* # Module: Example Tests */
/* ## Test: Example test */
/* PASS: assert-equal(1 + 1, 2) */

The JavaScript parser reads these comments and converts them into test runner calls.

Integration with CI/CD

// test/sass.test.js for CI
const path = require('node:path');
const sassTrue = require('sass-true');

// Ensure tests fail CI on assertion failures
process.on('unhandledRejection', (error) => {
  console.error('Unhandled promise rejection:', error);
  process.exit(1);
});

const sassFile = path.join(__dirname, 'sass/test.scss');
sassTrue.runSass({ describe, it }, sassFile);

Sass File Requirements

Your Sass test files should include True and define tests:

// test/sass/test.scss
@use 'true' as *;

@include describe('My Component') {
  @include it('should have correct default styles') {
    @include assert {
      @include output {
        @include my-component;
      }
      @include expect {
        display: block;
        padding: 1rem;
      }
    }
  }
}

// Don't forget the report!
@include report;

Troubleshooting

Common Issues

  1. "Cannot find Dart Sass dependency"

    npm install --save-dev sass-embedded
    # or
    npm install --save-dev sass
  2. "compressed style not supported"

    // Don't use compressed style
    sassTrue.runSass({ describe, it }, file, { 
      style: 'expanded'  // Use expanded (default)
    });
  3. Jest environment issues

    {
      "testEnvironment": "jest-environment-node-single-context"
    }
  4. Import resolution issues

    sassTrue.runSass({ describe, it }, file, {
      loadPaths: ['./sass', './node_modules'],
      importers: [/* custom importers */]
    });

Install with Tessl CLI

npx tessl i tessl/npm-sass-true

docs

css-output-testing.md

error-handling.md

index.md

javascript-integration.md

test-structure.md

value-assertions.md

tile.json