{"id":13550336,"url":"https://github.com/oddbird/true","last_synced_at":"2025-12-16T14:02:14.768Z","repository":{"id":8831422,"uuid":"10533455","full_name":"oddbird/true","owner":"oddbird","description":"Sass unit tests","archived":false,"fork":false,"pushed_at":"2025-12-03T21:56:04.000Z","size":8454,"stargazers_count":681,"open_issues_count":8,"forks_count":47,"subscribers_count":21,"default_branch":"main","last_synced_at":"2025-12-04T07:20:24.199Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://oddbird.net/true/","language":"SCSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oddbird.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"oddbird","open_collective":"oddbird-open-source"}},"created_at":"2013-06-06T18:32:13.000Z","updated_at":"2025-12-01T17:49:25.000Z","dependencies_parsed_at":"2025-12-16T04:09:17.364Z","dependency_job_id":null,"html_url":"https://github.com/oddbird/true","commit_stats":{"total_commits":488,"total_committers":32,"mean_commits":15.25,"dds":0.6536885245901639,"last_synced_commit":"d75d1c3bea60df890615efe1739d38356d07d21d"},"previous_names":["ericam/true"],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/oddbird/true","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oddbird%2Ftrue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oddbird%2Ftrue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oddbird%2Ftrue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oddbird%2Ftrue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oddbird","download_url":"https://codeload.github.com/oddbird/true/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oddbird%2Ftrue/sbom","scorecard":{"id":380063,"data":{"date":"2025-08-11","repo":{"name":"github.com/oddbird/true","commit":"cfcd111acf0c168fd2f02cfa0c2a939dd199c189"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: jobLevel 'contents' permission set to 'write': .github/workflows/publish-docs.yml:18","Warn: no topLevel permission defined: .github/workflows/publish-docs.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":10,"reason":"20 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":6,"reason":"Found 3/5 approved changesets -- score normalized to 6","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-docs.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/publish-docs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-docs.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/publish-docs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-docs.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/publish-docs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish-docs.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/publish-docs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/oddbird/true/test.yml/main?enable=pin","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 28 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-pfq8-rq6v-vf5m","Warn: Project is vulnerable to: GHSA-ch52-vgq2-943f","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx","Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T15:12:27.495Z","repository_id":8831422,"created_at":"2025-08-18T15:12:27.496Z","updated_at":"2025-08-18T15:12:27.496Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27765951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-16T02:00:10.477Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:01:31.808Z","updated_at":"2025-12-16T14:02:14.762Z","avatar_url":"https://github.com/oddbird.png","language":"SCSS","funding_links":["https://github.com/sponsors/oddbird","https://opencollective.com/oddbird-open-source"],"categories":["SCSS","CSS"],"sub_categories":[],"readme":"# True\n\n[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n\n\u003e **true** (_verb_): To make even, accurate, or precise.\n\u003e _\"True the wheels of a bicycle. True up the sides of a door. True your Sass\n\u003e code before you deploy.\"_\n\n**True is a unit-testing tool for [Sass](https://sass-lang.com/) code.**\n\n- Write tests in plain Sass\n- Compile with Dart Sass\n- Optional [JavaScript test runner integration][js-runner] (e.g.\n  [Mocha](https://mochajs.org/), [Jest](https://jestjs.io/), or\n  [Vitest](https://vitest.dev/))\n\n## Installation\n\n### 1. Install via npm\n\n```bash\nnpm install --save-dev sass-true\n```\n\n### 2. Install Dart Sass (if needed)\n\nTrue requires **Dart Sass v1.45.0 or higher**:\n\n```bash\nnpm install --save-dev sass-embedded # or `sass`\n```\n\n### 3. Import in your Sass tests\n\n**With [Node.js package importer][pkg-importer]**:\n\n```scss\n@use 'pkg:sass-true' as *;\n```\n\n**With [JavaScript test runner][js-runner]:**\n\n```scss\n@use 'true' as *;\n```\n\n**Without package importer:**\n\n```scss\n// Path may vary based on your project structure\n@use '../node_modules/sass-true' as *;\n```\n\n[pkg-importer]: https://sass-lang.com/documentation/js-api/classes/nodepackageimporter/\n[js-runner]: #javascript-test-runner-integration\n\n## Configuration\n\nTrue has one configuration variable: **`$terminal-output`** (boolean, defaults\nto `true`)\n\n| Value            | Behavior                                                                                                        |\n| ---------------- | --------------------------------------------------------------------------------------------------------------- |\n| `true` (default) | Shows detailed terminal output for debugging and results. Best for standalone Sass compilation.                 |\n| `false`          | Disables Sass terminal output. Use with [JavaScript test runners][js-runner] (they handle their own reporting). |\n\n### Legacy `@import` Support\n\nIf you're still using `@import` instead of `@use`, use the legacy import path\nwith the prefixed variable name:\n\n```scss\n// Path may vary\n@import '../node_modules/sass-true/sass/true';\n// Variable is named $true-terminal-output\n```\n\n## Usage\n\nTrue uses familiar testing syntax inspired by JavaScript test frameworks:\n\n### Testing Values (Functions \u0026 Variables)\n\nTrue can compare Sass values during compilation:\n\n```scss\n@include describe('Zip [function]') {\n  @include it('Zips multiple lists into a single multi-dimensional list') {\n    // Assert the expected results\n    @include assert-equal(zip(a b c, 1 2 3), (a 1, b 2, c 3));\n  }\n}\n```\n\n**Alternative syntax** using `test-module` and `test`:\n\n```scss\n@include test-module('Zip [function]') {\n  @include test('Zips multiple lists into a single multi-dimensional list') {\n    // Assert the expected results\n    @include assert-equal(zip(a b c, 1 2 3), (a 1, b 2, c 3));\n  }\n}\n```\n\n### Testing CSS Output (Mixins)\n\nCSS output tests require a different assertion structure, with an outer `assert`\nmixin, and a matching pair of `output` and `expect` to contain the\noutput-values:\n\n```scss\n// Test CSS output from mixins\n@include it('Outputs a font size and line height based on keyword') {\n  @include assert {\n    @include output {\n      @include font-size('large');\n    }\n\n    @include expect {\n      font-size: 2rem;\n      line-height: 3rem;\n    }\n  }\n}\n```\n\n\u003e **Note:** CSS output is compared after compilation. You can review changes\n\u003e manually with `git diff` or use a [JavaScript test runner][js-runner] for\n\u003e automated comparison.\n\n### Optional Summary Report\n\nDisplay a test summary in CSS output and/or terminal:\n\n```scss\n@include report;\n```\n\n### Documentation \u0026 Changelog\n\n- **[Full Documentation](https://www.oddbird.net/true/docs/)** – Complete API\n  reference and guides\n- **[CHANGELOG.md](https://github.com/oddbird/true/blob/main/CHANGELOG.md)** –\n  Migration notes for upgrading\n\n## JavaScript Test Runner Integration\n\nIntegrate True with your existing JS test runner for enhanced reporting and\nautomated CSS output comparison.\n\n### Quick Start\n\n#### 1. Install dependencies\n\n```bash\nnpm install --save-dev sass-true\nnpm install --save-dev sass-embedded # or `sass` (if not already installed)\n```\n\n#### 2. Write Sass tests\n\nCreate your Sass test file (e.g., `test/test.scss`) using True's syntax (see\n[Usage](#usage)).\n\n#### 3. Create JS test file\n\nCreate a JavaScript shim to run your Sass tests (e.g., `test/sass.test.js`):\n\n```js\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport sassTrue from 'sass-true';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst sassFile = path.join(__dirname, 'test.scss');\nsassTrue.runSass({ describe, it }, sassFile);\n```\n\n#### 4. Run your tests\n\nRun Mocha, Jest, Vitest, or your test runner. Sass tests will appear in the\nterminal output.\n\n### Enable Watch Mode for Sass Files\n\nBy default, `vitest --watch` and `jest --watch` don't detect Sass file changes.\n\n**Vitest solution:** Add Sass files to `forceRerunTriggers`:\n\n```js\n// vitest.config.js\nmodule.exports = defineConfig({\n  test: {\n    forceRerunTriggers: ['**/*.scss'],\n  },\n});\n```\n\nSee [Vitest documentation](https://vitest.dev/config/forcereruntriggers.html#forcereruntriggers) for details.\n\n**Jest solution:** Add `\"scss\"` to `moduleFileExtensions`:\n\n```js\n// jest.config.js\nmodule.exports = {\n  moduleFileExtensions: ['js', 'json', 'scss'],\n};\n```\n\nSee [Jest documentation](https://jestjs.io/docs/configuration#modulefileextensions-arraystring) for details.\n\n### Advanced Configuration\n\n#### `runSass()` API\n\n```js\nsassTrue.runSass(testRunnerConfig, sassPathOrSource, sassOptions);\n```\n\n**Arguments:**\n\n1. **`testRunnerConfig`** (object, required)\n\n   | Option         | Type                   | Required | Description                                                                                          |\n   | -------------- | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------- |\n   | `describe`     | function               | Yes      | Your test runner's `describe` function                                                               |\n   | `it`           | function               | Yes      | Your test runner's `it` function                                                                     |\n   | `sass`         | string or object       | No       | Sass implementation name (`'sass'` or `'sass-embedded'`) or instance. Auto-detected if not provided. |\n   | `sourceType`   | `'string'` or `'path'` | No       | Set to `'string'` to compile inline Sass source instead of file path (default: `'path'`)             |\n   | `contextLines` | number                 | No       | Number of CSS context lines to show in parse errors (default: `10`)                                  |\n\n2. **`sassPathOrSource`** (`'string'` or `'path'`, required)\n   - File path to Sass test file, or\n   - Inline Sass source code (if `sourceType: 'string'`)\n\n3. **`sassOptions`** (object, optional)\n   - Standard [Sass compile options](https://sass-lang.com/documentation/js-api/interfaces/Options)\n     (`importers`, `loadPaths`, `style`, etc.)\n   - **Default modifications by True:**\n     - `loadPaths`: True's sass directory is automatically added (allowing `@use 'true';`)\n     - `importers`: [Node.js package importer][pkg-importer] added if\n       `importers` is not defined and Dart Sass ≥ v1.71 (allowing `@use 'pkg:sass-true' as *;`)\n   - ⚠️ **Warning:** Must use `style: 'expanded'` (default).\n     `style: 'compressed'` is not supported.\n\n#### Multiple Test Files\n\nCall `runSass()` multiple times to run separate test files:\n\n```js\nsassTrue.runSass({ describe, it }, path.join(__dirname, 'functions.test.scss'));\nsassTrue.runSass({ describe, it }, path.join(__dirname, 'mixins.test.scss'));\n```\n\n#### Other Test Runners\n\nAny test runner with `describe`/`it` functions (or equivalents) works with True:\n\n```js\n// Example with custom test runner\nimport { suite, test } from 'my-test-runner';\n\nsassTrue.runSass(\n  { describe: suite, it: test },\n  path.join(__dirname, 'test.scss'),\n);\n```\n\n#### Custom Importers\n\nIf you use custom import syntax (e.g., tilde notation\n`@use '~accoutrement/sass/tools'`), you'll need to provide a\n[custom importer](https://sass-lang.com/documentation/js-api/interfaces/FileImporter):\n\n```js\nimport path from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport sassTrue from 'sass-true';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst importers = [\n  {\n    findFileUrl(url) {\n      if (!url.startsWith('~')) {\n        return null;\n      }\n      return new URL(\n        pathToFileURL(path.resolve('node_modules', url.substring(1))),\n      );\n    },\n  },\n];\n\nconst sassFile = path.join(__dirname, 'test.scss');\nsassTrue.runSass({ describe, it }, sassFile, { importers });\n```\n\n---\n\n## Sponsor OddBird's Open Source Work\n\nAt OddBird, we love contributing to the languages and tools developers rely on.\nWe're currently working on:\n\n- Polyfills for new Popover \u0026 Anchor Positioning functionality\n- CSS specifications for functions, mixins, and responsive typography\n- Sass testing tools like True\n\n**Help us keep this work sustainable!** Sponsor logos and avatars are featured\non our [website](https://www.oddbird.net/true/#open-source-sponsors).\n\n**[→ Sponsor OddBird on Open Collective](https://opencollective.com/oddbird-open-source)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foddbird%2Ftrue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foddbird%2Ftrue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foddbird%2Ftrue/lists"}