1# sass-spec
2
3### A cross-implementation [Sass][] test suite
4
5* [Running Specs](#running-specs)
6  * [Dart Sass](#dart-sass)
7  * [LibSass](#libsass)
8* [Spec Structure](#spec-structure)
9  * [HRX](#hrx)
10  * [Specifying Warnings](#specifying-warnings)
11  * [Implementation-Specific Expectations](#implementation-specific-expectations)
12  * [Options](#options)
13    * [`:todo`](#todo)
14    * [`:warning_todo`](#warning_todo)
15    * [`:ignore_for`](#ignore_for)
16* [Spec Style](#spec-style)
17* [Interactive Mode](#interactive-mode)
18
19[Sass]: https://sass-lang.com
20
21[![Build Status](https://travis-ci.org/sass/sass-spec.svg)](https://travis-ci.org/sass/sass-spec)
22
23`sass-spec` is the official Sass test suite. It's used by all major Sass
24implementations to ensure that they correctly implement the language.
25
26## Running Specs
27
28Before running specs, you'll need to install [Node.js] 14.14 or newer. Then, from the root of
29this repo, run `npm install`.
30
31[Node.j]: https://nodejs.org/en/download/
32
33From there, it depends which implementation you're testing:
34
35### Dart Sass
36
37To run specs against [Dart Sass][], the reference implementation of Sass that's
38used for [the `sass` package][] on npm, you'll first need to install [Dart][].
39Then run:
40
41[Dart Sass]: https://sass-lang.com/dart-sass
42[the `sass` package]: https://npmjs.com/package/sass
43[Dart]: https://www.dartlang.org/
44
45```sh
46# If you already have a clone of the Dart Sass repo, you can use that instead.
47git clone https://github.com/sass/dart-sass
48(cd dart-sass; pub get)
49export DART_SASS_PATH=`pwd`/dart-sass
50
51npm run sass-spec -- --dart $DART_SASS_PATH
52```
53
54### LibSass
55
56To run specs against [LibSass][], the C++ Sass implementation that's used for
57[Node Sass][] and other languages' Sass wrappers, you'll need to be able to
58[build LibSass][]. Once you have all the build dependencies:
59
60[LibSass]: https://sass-lang.com/libsass
61[Node Sass]: https://npmjs.com/package/node-sass
62[build LibSass]: https://github.com/sass/libsass/blob/master/docs/build.md
63
64```sh
65# If you already have a clone of the LibSass repo, you can use that instead.
66git clone https://github.com/sass/libsass
67(cd libsass; ./script/bootstrap; make sassc)
68export SASSC_PATH=`pwd`/libsass/sassc/bin/sassc
69
70npm run sass-spec -- --impl libsass -c $SASSC_PATH
71```
72
73## Spec Structure
74
75Each spec is defined by a directory with an `input.scss` or `input.sass` file
76and either:
77
78* An `output.css` file, in which case the spec asserts that the Sass
79  implementation compiles the input to the output. These specs are known as
80  "success specs".
81* An `error` file, in which case the spec asserts that the Sass implementation
82  prints the error message to standard error and exits with a non-zero status
83  code when it compiles the input. These specs are known as "error specs".
84
85These files may also have [variants that are specific to individual
86implementations][].
87
88[variants that are specific to individual implementations]: #implementation-specific-expectations
89
90The path to the spec serves as the spec's name, which should tersely describe
91what it's testing. Additional explanation, if necessary, is included in a silent
92comment in the input file. Specs may also contain additional files that are used
93by the input file, as well as various other features which are detailed below.
94
95### HRX
96
97Most specs are stored in [HRX files][], which are human-readable plain-text
98archives that define a virtual filesystem. This format makes it easy for code
99reviewers to see the context of specs they're reviewing. The spec runner treats
100each HRX file as a directory with the same name as the file, minus `.hrx`. For
101example:
102
103[HRX files]: https://github.com/google/hrx#human-readable-archive-hrx
104
105```hrx
106<===> input.scss
107ul {
108  margin-left: 1em;
109  li {
110    list-style-type: none;
111  }
112}
113
114<===> output.css
115ul {
116  margin-left: 1em;
117}
118ul li {
119  list-style-type: none;
120}
121```
122
123HRX archives can also contain directories. This allows us to write multiple
124specs for the same feature in a single file rather than spreading them out
125across hundreds of separate tiny files. By convention, we include an HRX comment
126with 80 `=` characters between each spec to help keep them visually separate.
127For example:
128
129```hrx
130<===> unbracketed/input.scss
131a {b: is-bracketed(foo bar)}
132
133<===> unbracketed/output.scss
134a {b: false}
135
136<===>
137================================================================================
138<===> bracketed/input.scss
139a {b: is-bracketed([foo bar])}
140
141<===> bracketed/output.scss
142a {b: true}
143```
144
145Each HRX archive shouldn't be much longer than 500 lines. Once one gets too
146long, its subdirectories should be split out into separate archives beneath a
147physical directory. Conversely, if a given directory contains many small HRX
148archives, they should be merged together into one larger file. This helps ensure
149that the repo remains easy to navigate.
150
151The only specs that *aren't* written in HRX format are those that include
152invalid UTF-8 byte sequences. The HRX format is itself written in UTF-8, so it's
153unable to represent the files in these specs.
154
155### Specifying Warnings
156
157By default, Sass implementations are expected to emit nothing on standard error
158when executing a success spec. However, if a `warning` file is added to the spec
159directory, the spec will assert that the Sass implementation prints that warning
160message to standard error as well as compiling the output. This is used to test
161the behavior of the `@debug` and `@warn` rules, as well as various warnings
162(particularly deprecation warnings) emitted by the Sass implementation itself.
163
164Warnings can't be specified for error specs, since everything an implementation
165emits on standard error is considered part of the error message that's validated
166against `error`.
167
168### Implementation-Specific Expectations
169
170Sometimes different Sass implementations produce different but equally-valid CSS
171outputs or error messages for the same input. To accommodate this,
172implementation-specific output, error, and warning files may be created by
173adding `-dart-sass` or `-libsass` after the file's name (but before its
174extension, in the case of `output.css`).
175
176When a spec is running for an implementation with an implementations-specific
177expectation, the normal expectation is ignored completely in favor of the
178implementation-specific one. It's even possible (although rare) for one
179implementation to expect an input file to produce an error while another expects
180it to compile successfully.
181
182### Options
183
184Metadata for a spec and options for how it's run can be written in an
185`options.yml` file in the spec's directory. This file applies recursively to all
186specs within its directory, so it can be used to configure many specs at once.
187All options must begin with `:`.
188
189All options that are supported for new specs are listed below. A few additional
190legacy options exist that are no longer considered good style and will
191eventually be removed.
192
193#### `:todo`
194
195```yaml
196---
197:todo:
198- sass/libsass#2827
199```
200
201This option indicates implementations that should add support for a spec, but
202haven't done so yet. When running specs for a given implementation, all specs
203marked as `:todo` for that implementation are skipped by default. This ensures that
204the build remains green while clearly marking which specs are expected to pass
205eventually.
206
207Implementations can be (and should be) specified as shorthand GitHub issue
208references rather than plain names. This makes it easy to track whether the
209implementation has fixed the issue, and to see which specs correspond to which
210issue. When marking an issue as `:todo` for an implementation, please either
211find an existing issue to reference or file a new one.
212
213If the `--run-todo` flag is passed to `sass-spec.rb`, specs marked as `:todo`
214for the current implementation will be run, and their failures will be reported.
215
216If the `--probe-todo` flag is passed to `sass-spec.rb`, specs marked as `:todo`
217for the current implementation will be run, but a failure will be reported *only
218if those specs pass*. This is used to determine which specs need to have `:todo`
219removed once a feature has been implemented. This can be used in combination
220with [`--interactive`](#interactive-mode) to automatically remove `:todo`s for
221these specs.
222
223#### `:warning_todo`
224
225```yaml
226---
227:warning_todo:
228- sass/libsass#2834
229```
230
231This option works like [`:todo`](#todo), except instead of skipping the entire
232test for listed implementations it only skips validating [that spec's
233warnings](#specifying-warnings). The rest of the spec is run and verified as
234normal. This should not be used for error specs.
235
236#### `:ignore_for`
237
238```yaml
239---
240:ignore_for:
241- libsass
242```
243
244This option indicates implementations that are never expected to be compatible
245with a given spec. It's used for specs for old features that some but not all
246implementations have dropped support for.
247
248[which is deprecated]: http://sass.logdown.com/posts/7081811
249
250## Spec Style
251
252The specs in this repo accumulated haphazardly over the years from contributions
253from many different people, so there's not currently much by way of unified
254style or organization. However, all new specs should follow the [style
255guide](STYLE_GUIDE.md), and old specs should be migrated to be style-guide
256compliant whenever possible.
257
258## Interactive Mode
259
260If you pass `--interactive` to `npm run sass-spec`, it will run in interactive
261mode. In this mode, whenever a spec would fail, the spec runner stops and
262provides the user with a prompt that allows them to inspect the failure and
263determine how to handle it. This makes it easy to add [implementation-specific
264expectations][] or mark specs as [`:todo`](#todo). For example:
265
266```
267In test case: spec/core_functions/color/hsla/four_args/alpha_percent
268Output does not match expectation.
269i. Show me the input.
270d. show diff.
271O. Update expected output and pass test.
272I. Migrate copy of test to pass on libsass.
273T. Mark spec as todo for libsass.
274G. Ignore test for libsass FOREVER.
275f. Mark as failed.
276X. Exit testing.
277```
278
279[implementation-specific expectations]: #implementation-specific-expectations
280
281Any option can also be applied to all future occurences of that type of failure
282by adding `!` after it. For example, if you want to mark *all* failing specs as
283`:todo` for the current implementation you'd type `I!`.
284
285## Tests
286
287The unit tests for the spec runner are located in the `test/` directory. To run
288these unit tests, run:
289
290```sh
291npm run test
292```
293