0
# JavaScript Integration
1
2
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.
3
4
## Capabilities
5
6
### Main Integration Function
7
8
Compiles Sass files and runs tests through JavaScript test runners with automated result parsing.
9
10
```typescript { .api }
11
/**
12
* Compile Sass tests and run them through JavaScript test runners
13
* @param trueOptions - Configuration object with test runner functions and options
14
* @param src - Path to Sass file or Sass source string
15
* @param sassOptions - Options passed to Sass compiler
16
*/
17
function runSass(
18
trueOptions: TrueOptions,
19
src: string,
20
sassOptions?: any
21
): void;
22
23
interface TrueOptions {
24
/** Describe function from your test runner (e.g., Mocha's describe) */
25
describe: (description: string, fn: () => void) => void;
26
/** It function from your test runner (e.g., Mocha's it) */
27
it: (description: string, fn: () => void) => void;
28
/** Sass compiler instance or package name ('sass', 'sass-embedded') */
29
sass?: any | string;
30
/** Whether src parameter is a file path or source string */
31
sourceType?: 'path' | 'string';
32
/** Number of context lines to show in error messages */
33
contextLines?: number;
34
}
35
```
36
37
**Basic Usage Examples:**
38
39
```javascript
40
// Basic Mocha integration
41
const path = require('node:path');
42
const sassTrue = require('sass-true');
43
44
const sassFile = path.join(__dirname, 'test.scss');
45
sassTrue.runSass({ describe, it }, sassFile);
46
```
47
48
```javascript
49
// Jest integration
50
const path = require('node:path');
51
const sassTrue = require('sass-true');
52
53
describe('Sass Tests', () => {
54
const sassFile = path.join(__dirname, 'sass/test.scss');
55
sassTrue.runSass({ describe, it }, sassFile);
56
});
57
```
58
59
```typescript
60
// TypeScript with explicit types
61
import * as sassTrue from 'sass-true';
62
import * as path from 'node:path';
63
64
const sassFile = path.join(__dirname, 'test.scss');
65
sassTrue.runSass({ describe, it }, sassFile);
66
```
67
68
### Advanced Configuration
69
70
```javascript
71
const path = require('node:path');
72
const sassTrue = require('sass-true');
73
74
// With custom Sass compiler
75
sassTrue.runSass(
76
{
77
describe,
78
it,
79
sass: 'sass-embedded', // Specify compiler
80
contextLines: 15 // More error context
81
},
82
path.join(__dirname, 'test.scss'),
83
{
84
// Sass options
85
loadPaths: ['./styles', './node_modules'],
86
style: 'expanded', // Must be 'expanded', not 'compressed'
87
sourceMap: false
88
}
89
);
90
91
// Using source string instead of file
92
const sassSource = `
93
@use 'true' as *;
94
@include test('String source test') {
95
@include assert-equal(1 + 1, 2);
96
}
97
`;
98
99
sassTrue.runSass(
100
{
101
describe,
102
it,
103
sourceType: 'string' // Indicate source is a string
104
},
105
sassSource
106
);
107
```
108
109
### Custom Importers
110
111
For projects using custom import patterns (like tilde notation):
112
113
```javascript
114
const path = require('node:path');
115
const { pathToFileURL } = require('node:url');
116
const sassTrue = require('sass-true');
117
118
const importers = [
119
{
120
findFileUrl(url) {
121
if (!url.startsWith('~')) {
122
return null;
123
}
124
return new URL(
125
pathToFileURL(path.resolve('node_modules', url.substring(1)))
126
);
127
},
128
},
129
];
130
131
const sassFile = path.join(__dirname, 'test.scss');
132
sassTrue.runSass({ describe, it }, sassFile, { importers });
133
```
134
135
## Test Result Types
136
137
### Core Data Structures
138
139
The JavaScript integration parses CSS output into structured test results:
140
141
```typescript { .api }
142
interface Assertion {
143
/** Description of what is being asserted */
144
description: string;
145
/** Type of assertion (equal, output, contains, etc.) */
146
assertionType?: string;
147
/** Actual output from the test */
148
output?: string;
149
/** Expected output for comparison */
150
expected?: string;
151
/** Additional details about the assertion */
152
details?: string;
153
/** Whether the assertion passed */
154
passed?: boolean;
155
/** Additional assertion-specific properties */
156
[key: string]: boolean | string | undefined;
157
}
158
159
interface Test {
160
/** Name/description of the test */
161
test: string;
162
/** Array of assertions within this test */
163
assertions: Assertion[];
164
}
165
166
interface Module {
167
/** Name/description of the module */
168
module: string;
169
/** Tests directly within this module */
170
tests?: Test[];
171
/** Nested sub-modules */
172
modules?: Module[];
173
}
174
```
175
176
### CSS Output Parsing
177
178
```typescript { .api }
179
/**
180
* Parse Sass-compiled CSS output containing True test results
181
* @param rawCss - CSS string output from Sass compilation
182
* @param contextLines - Number of context lines to show in error messages (default: 10)
183
* @returns Array of parsed test modules with their results
184
*/
185
function parse(rawCss: string, contextLines?: number): Module[];
186
```
187
188
**Usage Example:**
189
190
```javascript
191
const sass = require('sass');
192
const sassTrue = require('sass-true');
193
const path = require('node:path');
194
195
// Compile Sass file manually
196
const sassFile = path.join(__dirname, 'test.scss');
197
const result = sass.compile(sassFile);
198
199
// Parse the CSS output for test results
200
const modules = sassTrue.parse(result.css);
201
202
// Process the parsed results
203
modules.forEach(module => {
204
console.log(`Module: ${module.module}`);
205
module.tests?.forEach(test => {
206
console.log(` Test: ${test.test}`);
207
test.assertions.forEach(assertion => {
208
const status = assertion.passed ? 'PASS' : 'FAIL';
209
console.log(` ${status}: ${assertion.description}`);
210
});
211
});
212
});
213
```
214
215
### Failure Message Formatting
216
217
```typescript { .api }
218
/**
219
* Format detailed failure messages with diffs for failed assertions
220
* @param assertion - The failed assertion object
221
* @returns Formatted error message with visual diff
222
*/
223
function formatFailureMessage(assertion: Assertion): string;
224
```
225
226
**Usage Example:**
227
228
```javascript
229
// This is handled automatically by runSass, but you can use it manually:
230
const sassTrue = require('sass-true');
231
232
const failedAssertion = {
233
description: 'Should match expected output',
234
assertionType: 'equal',
235
output: 'color: red;',
236
expected: 'color: blue;',
237
passed: false
238
};
239
240
console.log(sassTrue.formatFailureMessage(failedAssertion));
241
// Outputs formatted diff showing expected vs actual
242
```
243
244
## Framework-Specific Setup
245
246
### Mocha Integration
247
248
```javascript
249
// test/sass.test.js
250
const path = require('node:path');
251
const sassTrue = require('sass-true');
252
253
// Single test file
254
const sassFile = path.join(__dirname, 'sass/test.scss');
255
sassTrue.runSass({ describe, it }, sassFile);
256
257
// Multiple test files
258
const testFiles = [
259
'sass/utilities.test.scss',
260
'sass/components.test.scss',
261
'sass/mixins.test.scss'
262
];
263
264
testFiles.forEach(file => {
265
const sassFile = path.join(__dirname, file);
266
sassTrue.runSass({ describe, it }, sassFile);
267
});
268
```
269
270
### Jest Integration
271
272
```javascript
273
// test/sass.test.js
274
const path = require('node:path');
275
const sassTrue = require('sass-true');
276
277
describe('Sass Tests', () => {
278
const sassFile = path.join(__dirname, 'sass/test.scss');
279
sassTrue.runSass({ describe, it }, sassFile);
280
});
281
```
282
283
**Jest Configuration:**
284
285
```json
286
// jest.config.js
287
module.exports = {
288
testEnvironment: 'jest-environment-node-single-context',
289
moduleFileExtensions: ['js', 'json', 'scss'],
290
// Other Jest config...
291
};
292
```
293
294
### Other Test Runners
295
296
Any test runner with `describe` and `it` functions can be used:
297
298
```javascript
299
// Using AVA (adapted)
300
const test = require('ava');
301
const sassTrue = require('sass-true');
302
303
// Adapter for AVA
304
const avaDescribe = (name, fn) => {
305
// AVA doesn't have describe, so we just run the function
306
fn();
307
};
308
309
const avaIt = (name, fn) => {
310
test(name, t => {
311
try {
312
fn();
313
t.pass();
314
} catch (error) {
315
t.fail(error.message);
316
}
317
});
318
};
319
320
sassTrue.runSass({
321
describe: avaDescribe,
322
it: avaIt
323
}, './test.scss');
324
```
325
326
## Advanced Usage
327
328
### Error Handling and Debugging
329
330
```javascript
331
const sassTrue = require('sass-true');
332
333
try {
334
sassTrue.runSass({ describe, it }, './test.scss', {
335
// More context lines for debugging
336
contextLines: 20
337
});
338
} catch (error) {
339
console.error('Sass compilation failed:', error.message);
340
341
if (error.message.includes('Context')) {
342
console.log('Check your Sass syntax and imports');
343
}
344
}
345
```
346
347
### Multiple Sass Compilers
348
349
```javascript
350
// Test with different Sass implementations
351
const configs = [
352
{ sass: 'sass', name: 'Dart Sass' },
353
{ sass: 'sass-embedded', name: 'Embedded Dart Sass' }
354
];
355
356
configs.forEach(config => {
357
describe(`Sass Tests with ${config.name}`, () => {
358
sassTrue.runSass(
359
{ describe, it, sass: config.sass },
360
'./test.scss'
361
);
362
});
363
});
364
```
365
366
### CSS Output Parsing
367
368
The JavaScript integration automatically parses special CSS comments generated by Sass True:
369
370
```scss
371
// This Sass test:
372
@include test('Example test') {
373
@include assert-equal(1 + 1, 2);
374
}
375
376
// Generates CSS comments like:
377
/* # Module: Example Tests */
378
/* ## Test: Example test */
379
/* PASS: assert-equal(1 + 1, 2) */
380
```
381
382
The JavaScript parser reads these comments and converts them into test runner calls.
383
384
### Integration with CI/CD
385
386
```javascript
387
// test/sass.test.js for CI
388
const path = require('node:path');
389
const sassTrue = require('sass-true');
390
391
// Ensure tests fail CI on assertion failures
392
process.on('unhandledRejection', (error) => {
393
console.error('Unhandled promise rejection:', error);
394
process.exit(1);
395
});
396
397
const sassFile = path.join(__dirname, 'sass/test.scss');
398
sassTrue.runSass({ describe, it }, sassFile);
399
```
400
401
## Sass File Requirements
402
403
Your Sass test files should include True and define tests:
404
405
```scss
406
// test/sass/test.scss
407
@use 'true' as *;
408
409
@include describe('My Component') {
410
@include it('should have correct default styles') {
411
@include assert {
412
@include output {
413
@include my-component;
414
}
415
@include expect {
416
display: block;
417
padding: 1rem;
418
}
419
}
420
}
421
}
422
423
// Don't forget the report!
424
@include report;
425
```
426
427
## Troubleshooting
428
429
### Common Issues
430
431
1. **"Cannot find Dart Sass dependency"**
432
```bash
433
npm install --save-dev sass-embedded
434
# or
435
npm install --save-dev sass
436
```
437
438
2. **"compressed style not supported"**
439
```javascript
440
// Don't use compressed style
441
sassTrue.runSass({ describe, it }, file, {
442
style: 'expanded' // Use expanded (default)
443
});
444
```
445
446
3. **Jest environment issues**
447
```json
448
{
449
"testEnvironment": "jest-environment-node-single-context"
450
}
451
```
452
453
4. **Import resolution issues**
454
```javascript
455
sassTrue.runSass({ describe, it }, file, {
456
loadPaths: ['./sass', './node_modules'],
457
importers: [/* custom importers */]
458
});
459
```