True: Unit Testing for Sass

repository·main·Indexed 20 days ago

https://github.com/oddbird/true

A unit-testing tool for Sass code that allows developers to write tests in plain Sass. It supports testing Sass values (functions and variables) and CSS output (mixins). True can be integrated into JavaScript test runners such as Vitest, Jest, or Mocha via the runSass() API. It requires Dart Sass v1.45.0 or higher and requires the 'expanded' output style for CSS comparison.

Tokens
3.3K
Snippets
10
Records
14
Agent score
72%

What's inside sass-true

  1. Integrate True with JavaScript test runners

    main

    You can run Sass tests within JS frameworks like Mocha, Jest, or Vitest by using the sass-true package to bridge the two.

    1. Create a JS test shim

    Create a file (e.g., test/sass.test.js) to invoke the Sass compilation:

    import path from 'node:path';
    import { fileURLToPath } from 'node:url';
    import sassTrue from 'sass-true';
    
    const __dirname = path.dirname(fileURLToPath(import.meta.url));
    const sassFile = path.join(__dirname, 'test.scss');
    
    // Pass your runner's describe/it functions to runSass
    sassTrue.runSass({ describe, it }, sassFile);

    2. Configure Watch Mode

    Standard JS runners may not detect .scss changes automatically.

    • Vitest: Add Sass files to forceRerunTriggers in vitest.config.js:
      test: { forceRerunTriggers: ['**/*.scss'] }
    • Jest: Add scss to moduleFileExtensions in jest.config.js:
      moduleFileExtensions: ['js', 'json', 'scss']
    import path from 'node:path';
    import { fileURLToPath } from 'node:url';
    import sassTrue from 'sass-true';
    
    const __dirname = path.dirname(fileURLToPath(import.meta.url));
    const sassFile = path.join(__dirname, 'test.scss');
    
    sassTrue.runSass({ describe, it }, sassFile);
  2. Install True for Sass testing

    main

    To use True, install the sass-true package and ensure you have Dart Sass v1.45.0 or higher installed (via sass-embedded or sass).

    1. Install via npm

    npm install --save-dev sass-true

    2. Install Dart Sass

    npm install --save-dev sass-embedded # or `sass`

    3. Import in your Sass tests

    Depending on your environment, use one of the following methods:

    • With Node.js package importer: @use 'pkg:sass-true' as *;
    • With a JavaScript test runner: @use 'true' as *;
    • Without package importer: @use '../node_modules/sass-true' as *; (path may vary)
    npm install --save-dev sass-true
    npm install --save-dev sass-embedded
  3. Configure terminal output in True

    main

    True uses the $terminal-output configuration variable to control how results are displayed.

    ValueBehavior
    true (default)Shows detailed terminal output for debugging and results. Best for standalone Sass compilation.
    falseDisables Sass terminal output. Use this when integrating with JavaScript test runners (e.g., Mocha, Jest, Vitest) that handle their own reporting.

    Legacy @import support: If using @import instead of @use, use the prefixed variable $true-terminal-output and the legacy path:

    @import '../node_modules/sass-true/sass/true';
    $true-terminal-output: false;
  4. Test Sass values (Functions & Variables)

    main

    True allows you to compare Sass values during compilation using describe, it, and assert-equal. You can use the standard syntax or the test-module/test alternative.

    Standard syntax:

    @include describe('Zip [function]') {
      @include it('Zips multiple lists into a single multi-dimensional list') {
        @include assert-equal(zip(a b c, 1 2 3), (a 1, b 2, c 3));
      }
    }

    Alternative syntax:

    @include test-module('Zip [function]') {
      @include test('Zips multiple lists into a single multi-dimensional list') {
        @include assert-equal(zip(a b c, 1 2 3), (a 1, b 2, c 3));
      }
    }
  5. Test CSS output from Mixins

    main

    To test the actual CSS generated by a mixin, use an assert block containing an output block (the code to run) and an expect block (the expected CSS properties).

    @include it('Outputs a font size and line height based on keyword') {
      @include assert {
        @include output {
          @include font-size('large');
        }
    
        @include expect {
          font-size: 2rem;
          line-height: 3rem;
        }
      }
    }

    Note: CSS output is compared after compilation. You can use a JavaScript test runner for automated comparison.

  6. Use the runSass() API

    main

    The runSass() function is the primary entry point for integrating True with JavaScript environments.

    Signature: sassTrue.runSass(testRunnerConfig, sassPathOrSource, sassOptions);

    Arguments

    1. testRunnerConfig (Object, Required)

    OptionTypeRequiredDescription
    describefunctionYesYour test runner's describe function
    itfunctionYesYour test runner's it function
    sassstring or objectNoSass implementation name ('sass' or 'sass-embedded') or instance.
    sourceType'string' or 'path'NoSet to 'string' to compile inline Sass source instead of file path (default: 'path')
    contextLinesnumberNoNumber of CSS context lines to show in parse errors (default: 10)

    2. sassPathOrSource ('string' or 'path', Required)

    • File path to the Sass test file, or
    • Inline Sass source code (if sourceType: 'string')

    3. sassOptions (Object, Optional)

    Standard Sass compile options (e.g., importers, loadPaths, style).

    Important Notes:

    • Style Requirement: You must use style: 'expanded'. style: 'compressed' is not supported.
    • Automatic Load Paths: True automatically adds its own Sass directory to loadPaths.
    • Automatic Importers: If using Dart Sass $\ge$ v1.71 and importers is not defined, the Node.js package importer is added automatically.
  7. Configure runSass() via TrueOptions

    main

    When calling runSass, you provide a TrueOptions object to configure the test runner integration and Sass compiler behavior.

    OptionTypeDescription
    describe(description: string, fn: () => void) => voidRequired. The test runner's describe function.
    it(description: string, fn: () => void) => voidRequired. The test runner's it (or test) function.
    sassanyOptional. A string representing the path to the Sass package, or the compiler object itself.
    sourceType'path' | 'string'Optional. Use 'string' if the src argument is a raw Sass string instead of a file path.
    contextLinesnumberOptional. Number of lines of context to show in error messages.
  8. Configure ESLint for the project

    main

    The project uses a flat configuration file (eslint.config.js) based on eslint/config. It integrates several plugins and presets to enforce code quality, TypeScript support, import ordering, and testing environment globals.

    Key Configurations

    • Global Ignores: The following directories and files are excluded from linting:

      • .git/*, .nyc_output/*, .vscode/*, .yarn/*, .yarnrc.yml, coverage/*, dist/*, docs/*, node_modules/*
    • General JavaScript/TypeScript Files: Applies to **/*.{js,mjs,cjs,ts,cts,mts}.

      • Uses typescript-eslint parser.
      • Globals: node and es2022.
      • parserOptions.sourceType is set to script.
      • Enforces import-x rules for import management.
    • Source Files: Applies to src/**/*.{js,mjs,cjs,ts,cts,mts}.

      • Uses typescript-eslint parser.
      • Globals: browser and es2022.
      • parserOptions.sourceType is set to module.
      • Uses simple-import-sort for strict import/export sorting.
      • Disables import-x/order to avoid conflicts with simple-import-sort.
    • Test Files: Applies to test/**/*.{js,ts}.

      • Uses typescript-eslint parser.
      • Globals: vitest environments, mocha, and es2022.
      • Includes vitest plugin and recommended Vitest rules.
      • Uses simple-import-sort for sorting.
  9. Import sass-true using @use

    main

    To use the sass-true library in your Sass files, use the @use rule. You can import it via the package name pkg:sass-true or by referencing the local file path true depending on your environment setup.

    This entrypoint forwards all functionality from the sass/true module, providing access to testing functions, variables, and mixins.

    @use 'pkg:sass-true';
    // or
    @use 'true';
  10. Parse Sass test files with parse()

    main

    The parse function takes raw compiled CSS and extracts the structured test data (modules, tests, and assertions) defined by the special comment tokens in the file. This is useful if you want to inspect the test structure manually without executing the tests through a runner.

    Returns an array of Module objects, representing the hierarchy of tests found in the CSS.

    import { parse } from 'true';
    
    const modules = parse(compiledCss, 10); // 10 is the contextLines
  11. Format assertion failure messages

    main

    If an assertion fails, formatFailureMessage generates a human-readable error message. It includes a unified diff between the expected and output values.

    For specific assertion types, it provides enhanced feedback:

    • contains-string: If multiple strings were expected, it lists which ones were found (✓) and which were missing (✗).
    • contains: If multiple CSS blocks were expected, it lists which blocks were found and which were missing, showing the expected block content for clarity.
    import { formatFailureMessage, type Assertion } from 'true';
    
    // Example usage within a test runner error handler
    const msg = formatFailureMessage(failedAssertion);
    throw new Error(msg);
  12. Run Sass tests with runSass()

    main

    The runSass function is the primary entry point for executing Sass tests. It takes a source string (or path), compiles it using a Sass implementation (like sass or sass-embedded), and then parses the resulting CSS to execute assertions defined within the Sass file via special comment tokens.

    Key Requirements:

    • Sass Implementation: True will attempt to automatically load sass-embedded or sass. You can also provide a specific compiler via TrueOptions.sass.
    • Output Style: You must not use style: "compressed" in your Sass options. True requires the default expanded output style to correctly parse and match CSS rules.
    • Test Runner Integration: You must provide describe and it functions in TrueOptions (e.g., from Jest, Mocha, or Vitest) to report test results.
    import { runSass } from 'true';
    
    runSass(
      {
        describe: describe, // e.g., from Jest
        it: it,             // e.g., from Jest
        sass: 'sass-embedded' // optional: specify compiler
      },
      'path/to/your/test.scss',
      { /* Sass options */ }
    );