Unit testing framework for Sass code with JavaScript test runner 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.
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);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
);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 });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[];
}/**
* 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}`);
});
});
});/**
* 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// 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);
});// 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...
};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');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');
}
}// 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'
);
});
});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.
// 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);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;"Cannot find Dart Sass dependency"
npm install --save-dev sass-embedded
# or
npm install --save-dev sass"compressed style not supported"
// Don't use compressed style
sassTrue.runSass({ describe, it }, file, {
style: 'expanded' // Use expanded (default)
});Jest environment issues
{
"testEnvironment": "jest-environment-node-single-context"
}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