From 8e06cfef251c0ff61a0aca006b63be5e65648e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kry=C5=A1p=C3=ADn?= Date: Wed, 25 Oct 2023 23:59:51 +0200 Subject: [PATCH] Test(web): Introduce SASS unit tests --- packages/web/CONTRIBUTING.md | 8 + packages/web/package.json | 3 + .../tools/__tests__/_accessibility.test.scss | 82 +++++++++ .../tools/__tests__/_breakpoint.test.scss | 83 +++++++++ .../tools/__tests__/_dictionaries.test.scss | 170 ++++++++++++++++++ .../tools/__tests__/_form-fields.test.scss | 57 ++++++ .../src/scss/tools/__tests__/_links.test.scss | 45 +++++ .../src/scss/tools/__tests__/_list.test.scss | 13 ++ .../scss/tools/__tests__/_placement.test.scss | 68 +++++++ .../src/scss/tools/__tests__/_reset.test.scss | 33 ++++ .../scss/tools/__tests__/_string.test.scss | 35 ++++ .../src/scss/tools/__tests__/_svg.test.scss | 25 +++ .../tools/__tests__/_typography.test.scss | 47 +++++ .../scss/tools/__tests__/_utilities.test.scss | 59 ++++++ .../web/src/scss/tools/_dictionaries.scss | 6 +- packages/web/src/scss/tools/_typography.scss | 33 ++-- packages/web/tests/scss.test.ts | 29 +++ yarn.lock | 61 ++++++- 18 files changed, 836 insertions(+), 21 deletions(-) create mode 100644 packages/web/src/scss/tools/__tests__/_accessibility.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_breakpoint.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_dictionaries.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_form-fields.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_links.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_list.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_placement.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_reset.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_string.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_svg.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_typography.test.scss create mode 100644 packages/web/src/scss/tools/__tests__/_utilities.test.scss create mode 100644 packages/web/tests/scss.test.ts diff --git a/packages/web/CONTRIBUTING.md b/packages/web/CONTRIBUTING.md index 578d650e09..bd4c626b9e 100644 --- a/packages/web/CONTRIBUTING.md +++ b/packages/web/CONTRIBUTING.md @@ -126,6 +126,13 @@ Now use the reference from the theme in component styles: - `% yarn test` for test package (lint, format, unit testing, types) - `% yarn test:unit` for unit tests +### SASS Unit Testing + +We use [sass-true][sass-true] for unit testing of our SASS components. The +tests are located in `src/scss/components//__tests__` and are +named `.test.scss`. The tests are run as a part of the Jest +testing suite. We aim to have 100% coverage of our mixins and functions. + ## Migrating Your Components to Spirit ### Code Conventions @@ -196,3 +203,4 @@ in a special way. Read more about it in [design-tokens-faq]: https://github.com/lmc-eu/spirit-design-system/tree/main/packages/design-tokens#faq [sass-modules]: https://sass-lang.com/blog/the-module-system-is-launched [es modules]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules +[sass-true]: https://github.com/oddbird/true diff --git a/packages/web/package.json b/packages/web/package.json index bcfc3575de..1e836d2cf7 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -66,12 +66,15 @@ "@types/jest": "29.5.6", "autoprefixer": "10.4.16", "clean-css-cli": "5.6.2", + "glob": "10.3.10", "jest-environment-jsdom": "29.7.0", + "jest-environment-node-single-context": "29.1.0", "postcss": "8.4.31", "postcss-cli": "10.1.0", "resize-observer-polyfill": "1.5.1", "rollup": "3.29.4", "sass": "1.69.4", + "sass-true": "7.0.0", "shx": "0.3.4", "stylelint": "15.11.0", "stylelint-config-prettier": "9.0.5", diff --git a/packages/web/src/scss/tools/__tests__/_accessibility.test.scss b/packages/web/src/scss/tools/__tests__/_accessibility.test.scss new file mode 100644 index 0000000000..f84223d787 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_accessibility.test.scss @@ -0,0 +1,82 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../accessibility'; + +@include true.describe('hide-text mixin') { + @include true.it('should output styles to visually hide text') { + @include true.assert { + @include true.output { + .text-hidden { + @include accessibility.hide-text(); + } + } + + @include true.expect { + .text-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + } + } + } +} + +@include true.describe('min-tap-target mixin') { + @include true.it('should output styles for default centered tap target') { + @include true.assert { + @include true.output { + .tap-target-centered { + @include accessibility.min-tap-target(40px); + } + } + + @include true.expect { + .tap-target-centered { + position: relative; + } + + // stylelint-disable order/properties-order -- Disabling rule due to conditional rendering affecting property order + .tap-target-centered::before { + content: ''; + position: absolute; + width: 40px; + height: 40px; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + // stylelint-enable order/properties-order + } + } + } + + @include true.it('should output styles for non-centered tap target') { + @include true.assert { + @include true.output { + .tap-target-non-centered { + @include accessibility.min-tap-target(40px, false); + } + } + + @include true.expect { + .tap-target-non-centered { + position: relative; + } + + .tap-target-non-centered::before { + content: ''; + position: absolute; + width: 40px; + height: 40px; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss b/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss new file mode 100644 index 0000000000..391f99d227 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_breakpoint.test.scss @@ -0,0 +1,83 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../breakpoint'; + +@include true.describe('up mixin from breakpoint') { + @include true.it('should output media query for provided breakpoint value') { + @include true.assert { + @include true.output { + @include breakpoint.up(600px) { + .selector { + color: #bada55; + } + } + } + + @include true.expect { + @media (min-width: 600px) { + .selector { + color: #bada55; + } + } + } + } + } + + @include true.it('should output styles directly without media query for 0 value') { + @include true.assert { + @include true.output { + @include breakpoint.up(0) { + .selector { + color: #bada55; + } + } + } + + @include true.expect { + .selector { + color: #bada55; + } + } + } + } +} + +@include true.describe('down mixin from breakpoint') { + @include true.it('should output media query for provided breakpoint value') { + @include true.assert { + @include true.output { + @include breakpoint.down(600px) { + .selector { + color: #bada55; + } + } + } + + @include true.expect { + @media (max-width: 599px) { + .selector { + color: #bada55; + } + } + } + } + } + + @include true.it('should output styles directly without media query for 0 value') { + @include true.assert { + @include true.output { + @include breakpoint.down(0) { + .selector { + color: #bada55; + } + } + } + + @include true.expect { + .selector { + color: #bada55; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss b/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss new file mode 100644 index 0000000000..ba2d4ea89f --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss @@ -0,0 +1,170 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses +@use 'true'; +@use '../../settings/dictionaries' as dictionaries-settings; +@use '../dictionaries'; +@use '@tokens' as tokens; + +@include true.describe('generate-colors mixin') { + @include true.it('should generate correct color classes based on a dictionary') { + @include true.assert { + @include true.output { + @include dictionaries.generate-colors( + 'Test', + ('primary'), + ( + color: 'default', + ) + ); + } + + @include true.expect { + .Test--primary { + --test-color: #29616f; + + color: var(--test-color); + } + } + } + } +} + +@include true.describe('generate-link-colors mixin') { + @include true.it('should generate correct link color classes based on a dictionary') { + @include true.assert { + @include true.output { + @include dictionaries.generate-link-colors( + '.link', + dictionaries-settings.$action-link-colors, + tokens.$action-colors, + (default, hover, active, disabled) + ); + } + + @include true.expect { + .link-primary { + color: #29616f; + } + + .link-primary:hover { + color: #1b5260; + } + + .link-primary:active { + color: #0b3a46; + } + + .link-primary.link-disabled:any-link { + color: #c4c4c4; + } + + .link-secondary { + color: #90a2a7; + } + + .link-secondary:hover { + color: #849499; + } + + .link-secondary:active { + color: #6e7b80; + } + + .link-secondary.link-disabled:any-link { + color: #c4c4c4; + } + + .link-inverted { + color: #e9e9e9; + } + + .link-inverted:hover { + color: #dbdbdb; + } + + .link-inverted:active { + color: #d4d4d4; + } + + .link-inverted.link-disabled:any-link { + color: #c4c4c4; + } + } + } + } +} + +@include true.describe('generate-placements mixin') { + @include true.it('should generate correct placement classes based on a dictionary') { + @include true.assert { + @include true.output { + @include dictionaries.generate-placements('Test', ('top-left')); + } + + @include true.expect { + .Test--topLeft { + --test-offset: 0; + + inset: auto auto 100% 0; + translate: var(--test-offset-orthogonal, 0) calc(-1 * var(--test-offset, 0)); + transform-origin: bottom right; + } + } + } + } +} + +// Create Test classes in order to test extend in next mixin test +.Test--topLeft, +.Test--bottomRight { + position: relative; +} + +@include true.describe('generate-controlled-placements mixin') { + @include true.it('should generate correct controlled placement classes based on a dictionary') { + @include true.assert { + @include true.output { + @include dictionaries.generate-controlled-placements( + 'Test', + ('top-left', 'bottom-right'), + 'data-placement' + ); + } + + @include true.expect { + // stylelint-disable scss/at-extend-no-missing-placeholder -- We are extending classes created by generate-placements(). + .Test[data-placement='topLeft'] { + @extend .Test--topLeft !optional; + } + + .Test[data-placement='bottomRight'] { + @extend .Test--bottomRight !optional; + } + // stylelint-enable scss/at-extend-no-missing-placeholder + } + } + } +} + +@include true.describe('generate-sizes mixin') { + @include true.it('should generate correct size classes based on a config') { + @include true.assert { + @include true.output { + @include dictionaries.generate-sizes( + 'TestSize', + ( + large: ( + padding-y: 12px, + padding-x: 16px, + ), + ) + ); + } + + @include true.expect { + .TestSize--large { + padding: 12px 16px; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_form-fields.test.scss b/packages/web/src/scss/tools/__tests__/_form-fields.test.scss new file mode 100644 index 0000000000..afcecc98b4 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_form-fields.test.scss @@ -0,0 +1,57 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../../settings/cursors'; +@use '../../theme/form-fields' as form-fields-theme; +@use '../form-fields'; + +@include true.describe('form-fields mixins') { + @include true.it('should return label-disabled mixin styles') { + @include true.assert { + @include true.output { + .label-disabled-test { + @include form-fields.label-disabled(); + } + } + @include true.expect { + .label-disabled-test { + color: form-fields-theme.$label-color-disabled; + + &::after { + color: form-fields-theme.$label-color-disabled; + } + } + } + } + } + + @include true.it('should return input-disabled mixin styles') { + @include true.assert { + @include true.output { + .input-disabled-test { + @include form-fields.input-disabled(); + } + } + @include true.expect { + .input-disabled-test { + color: form-fields-theme.$input-color-disabled; + cursor: cursors.$disabled; + } + } + } + } + + @include true.it('should return box-field-fluid mixin styles') { + @include true.assert { + @include true.output { + .box-field-fluid-test { + @include form-fields.box-field-fluid(); + } + } + @include true.expect { + .box-field-fluid-test { + width: 100%; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_links.test.scss b/packages/web/src/scss/tools/__tests__/_links.test.scss new file mode 100644 index 0000000000..02b27c50d3 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_links.test.scss @@ -0,0 +1,45 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../links'; + +@include true.describe('stretch mixin') { + @include true.it('should output default styles for ::before pseudo-element') { + @include true.assert { + @include true.output { + .test { + @include links.stretch(); + } + } + + @include true.expect { + .test::before { + content: ''; + position: absolute; + z-index: 0; + display: block; + inset: 0; + } + } + } + } + + @include true.it('should output styles for provided pseudo-element') { + @include true.assert { + @include true.output { + .test { + @include links.stretch('after'); + } + } + + @include true.expect { + .test::after { + content: ''; + position: absolute; + z-index: 0; + display: block; + inset: 0; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_list.test.scss b/packages/web/src/scss/tools/__tests__/_list.test.scss new file mode 100644 index 0000000000..be9b716719 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_list.test.scss @@ -0,0 +1,13 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../list'; + +@include true.describe('to-string function') { + @include true.it('should convert a list to a string with the specified separator') { + @include true.assert-equal(list.to-string((1, 2, 3), '-'), '1-2-3'); + + @include true.assert-equal(list.to-string(('a', 'b', 'c'), '_'), 'a_b_c'); + + @include true.assert-equal(list.to-string(('apple', 'banana', 'cherry')), 'apple banana cherry'); + } +} diff --git a/packages/web/src/scss/tools/__tests__/_placement.test.scss b/packages/web/src/scss/tools/__tests__/_placement.test.scss new file mode 100644 index 0000000000..729d31f0cf --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_placement.test.scss @@ -0,0 +1,68 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../placement'; + +@include true.describe('placement functions and mixins') { + @include true.it('should returns correct values for inverse function') { + @include true.assert { + @include true.output { + $inverse-top: placement.inverse('top'); + $inverse-bottom-left: placement.inverse('bottom-left'); + } + @include true.expect { + $inverse-top: 'bottom'; + $inverse-bottom-left: 'top-right'; + } + } + } + + @include true.it('should return parent position') { + @include true.assert { + @include true.output { + .parent-test { + @include placement.parent(); + } + } + @include true.expect { + .parent-test { + position: relative; + } + } + } + } + + @include true.it('should return child position with z-index') { + @include true.assert { + @include true.output { + .child-test { + @include placement.child(2); + } + } + @include true.expect { + .child-test { + position: absolute; + z-index: 2; + } + } + } + } + + @include true.it('should return child-variant with correct styles') { + @include true.assert { + @include true.output { + .child-variant-test { + @include placement.child-variant('test', 'top', 10px); + } + } + @include true.expect { + .child-variant-test { + --test-offset: 10px; + + inset: auto auto 100% 50%; + translate: var(--test-offset-orthogonal, -50%) calc(-1 * var(--test-offset, 0)); + transform-origin: bottom; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_reset.test.scss b/packages/web/src/scss/tools/__tests__/_reset.test.scss new file mode 100644 index 0000000000..25177904ed --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_reset.test.scss @@ -0,0 +1,33 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../../settings/cursors'; +@use '../reset'; + +@include true.describe('button mixin') { + @include true.it('should apply reset styles for a button') { + @include true.assert { + @include true.output { + .btn-test { + @include reset.button(); + } + } + + @include true.expect { + .btn-test { + appearance: none; + display: inline-flex; + flex: none; + align-items: center; + justify-content: center; + padding: 0; + font: inherit; + border: none; + border-radius: 0; + background: none; + box-shadow: none; + cursor: cursors.$button-links; + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_string.test.scss b/packages/web/src/scss/tools/__tests__/_string.test.scss new file mode 100644 index 0000000000..b739bacbf3 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_string.test.scss @@ -0,0 +1,35 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../string'; + +@include true.describe('convert-kebab-case-to-camel-case function') { + @include true.it('should convert kebab-case to camelCase') { + @include true.assert-equal(string.convert-kebab-case-to-camel-case('top-left'), 'topLeft'); + + @include true.assert-equal( + string.convert-kebab-case-to-camel-case('my-long-variable-name'), + 'myLongVariableName' + ); + } +} + +@include true.describe('convert-pascal-case-to-kebab-case function') { + @include true.it('should convert PascalCase to kebab-case') { + @include true.assert-equal(string.convert-pascal-case-to-kebab-case('TopLeft'), 'top-left'); + + @include true.assert-equal( + string.convert-pascal-case-to-kebab-case('MyLongVariableName'), + 'my-long-variable-name' + ); + } +} + +@include true.describe('replace function') { + @include true.it('should replace all occurrences of a substring with another string') { + @include true.assert-equal(string.replace('top-left', '-', ''), 'topleft'); + + @include true.assert-equal(string.replace('some-other-text', 'e', 'a'), 'soma-othar-taxt'); + + @include true.assert-equal(string.replace('no-replacement-here', 'z', 'y'), 'no-replacement-here'); + } +} diff --git a/packages/web/src/scss/tools/__tests__/_svg.test.scss b/packages/web/src/scss/tools/__tests__/_svg.test.scss new file mode 100644 index 0000000000..025bdfbc41 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_svg.test.scss @@ -0,0 +1,25 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../svg'; + +@include true.describe('escape function') { + @include true.it('should escape characters within an SVG string not wrapped in url()') { + $input: 'data:image/svg+xml,'; + $expected-output: 'data:image/svg+xml,%3Cpath%20d=%22M10%2010%22%3E%3C/path%3E'; + + @include true.assert { + @include true.output(svg.escape($input)); + @include true.expect($expected-output); + } + } + + @include true.it('should escape characters within an SVG string wrapped in url()') { + $input: url('data:image/svg+xml,'); + $expected-output: url('data:image/svg+xml,%3Cpath%20d=%22M10%2010%22%3E%3C/path%3E'); + + @include true.assert { + @include true.output(svg.escape($input)); + @include true.expect($expected-output); + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_typography.test.scss b/packages/web/src/scss/tools/__tests__/_typography.test.scss new file mode 100644 index 0000000000..5f2d26c825 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_typography.test.scss @@ -0,0 +1,47 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'sass:map'; +@use 'true'; +@use '../typography'; +@use '@tokens' as tokens; + +@include true.describe('generate mixin') { + @include true.it('should generate typography styles for a given token and breakpoint') { + $sample-token: ( + 'tablet': ( + 'font-family': 'Arial, sans-serif', + 'font-size': 16px, + 'font-style': italic, + 'font-weight': 700, + 'text-transform': uppercase, + 'text-decoration': underline, + 'text-indent': 2em, + 'line-height': 1.5, + 'letter-spacing': 0.05em, + ), + ); + + @include true.assert { + @include true.output { + .typography-test { + @include typography.generate($sample-token); + } + } + + @include true.expect { + @media (min-width: map.get(tokens.$breakpoints, 'tablet')) { + .typography-test { + font-style: italic; + font-weight: 700; + font-size: 16px; + line-height: 1.5; + font-family: Arial, sans-serif; + letter-spacing: 0.05em; + text-decoration: underline; + text-transform: uppercase; + text-indent: 2em; + } + } + } + } + } +} diff --git a/packages/web/src/scss/tools/__tests__/_utilities.test.scss b/packages/web/src/scss/tools/__tests__/_utilities.test.scss new file mode 100644 index 0000000000..80a4ff7ef3 --- /dev/null +++ b/packages/web/src/scss/tools/__tests__/_utilities.test.scss @@ -0,0 +1,59 @@ +// stylelint-disable scss/at-mixin-argumentless-call-parentheses -- We need to allow this to make sass-true work +@use 'true'; +@use '../utilities'; + +@include true.describe('generate mixin') { + @include true.it('should generate utility classes based on the provided utility map and infix') { + $sample-utility: ( + property: display, + class: d, + values: ( + block, + flex, + grid, + inline, + inline-block, + inline-flex, + none, + ), + ); + + @include true.assert { + @include true.output { + @include utilities.generate($sample-utility, ''); + } + + @include true.expect { + // stylelint-disable declaration-no-important -- We need to allow this to make sass-true work + .d-block { + display: block !important; + } + + .d-flex { + display: flex !important; + } + + .d-grid { + display: grid !important; + } + + .d-inline { + display: inline !important; + } + + .d-inline-block { + display: inline-block !important; + } + + .d-inline-flex { + display: inline-flex !important; + } + + .d-none { + display: none !important; + } + // stylelint-enable declaration-no-important + } + } + } +} diff --git a/packages/web/src/scss/tools/_dictionaries.scss b/packages/web/src/scss/tools/_dictionaries.scss index 0b7431b3b0..f9c268c187 100644 --- a/packages/web/src/scss/tools/_dictionaries.scss +++ b/packages/web/src/scss/tools/_dictionaries.scss @@ -182,7 +182,7 @@ .#{$class-name}[#{$data-attribute}='#{$placement-modifier}'] { // stylelint-disable scss/at-extend-no-missing-placeholder -- We are extending classes created by generate-placements(). - @extend .#{$class-name}--#{$placement-modifier}; + @extend .#{$class-name}--#{$placement-modifier} !optional; } } } @@ -195,11 +195,11 @@ @mixin generate-sizes($class-name, $variables) { @each $size, $variables in $variables { .#{$class-name}--#{$size} { + padding: map.get($variables, padding-y) map.get($variables, padding-x); + @if map.has-key($variables, typography) { @include typography.generate(map.get($variables, typography)); } - - padding: map.get($variables, padding-y) map.get($variables, padding-x); } } } diff --git a/packages/web/src/scss/tools/_typography.scss b/packages/web/src/scss/tools/_typography.scss index 76d8095756..541b1e6cb8 100644 --- a/packages/web/src/scss/tools/_typography.scss +++ b/packages/web/src/scss/tools/_typography.scss @@ -6,41 +6,40 @@ @mixin generate($token) { @each $breakpoint-name, $breakpoint-value in tokens.$breakpoints { @include breakpoint.up($breakpoint-value) { - @if map.has-key($token, $breakpoint-name, font-family) { - font-family: string.unquote(map.get($token, $breakpoint-name, font-family)); + @if map.has-key($token, $breakpoint-name, font-style) { + font-style: map.get($token, $breakpoint-name, font-style); + } + + @if map.has-key($token, $breakpoint-name, font-weight) { + font-weight: map.get($token, $breakpoint-name, font-weight); } @if map.has-key($token, $breakpoint-name, font-size) { font-size: map.get($token, $breakpoint-name, font-size); } - @if map.has-key($token, $breakpoint-name, font-style) { - font-style: map.get($token, $breakpoint-name, font-style); + @if map.has-key($token, $breakpoint-name, line-height) { + line-height: map.get($token, $breakpoint-name, line-height); } - @if map.has-key($token, $breakpoint-name, font-weight) { - // stylelint-disable-next-line font-weight-notation - font-weight: map.get($token, $breakpoint-name, font-weight); + @if map.has-key($token, $breakpoint-name, font-family) { + font-family: string.unquote(map.get($token, $breakpoint-name, font-family)); } - @if map.has-key($token, $breakpoint-name, text-transform) { - text-transform: map.get($token, $breakpoint-name, text-transform); + @if map.has-key($token, $breakpoint-name, letter-spacing) { + letter-spacing: map.get($token, $breakpoint-name, letter-spacing); } @if map.has-key($token, $breakpoint-name, text-decoration) { text-decoration: map.get($token, $breakpoint-name, text-decoration); } - @if map.has-key($token, $breakpoint-name, text-indent) { - text-indent: map.get($token, $breakpoint-name, text-indent); - } - - @if map.has-key($token, $breakpoint-name, line-height) { - line-height: map.get($token, $breakpoint-name, line-height); + @if map.has-key($token, $breakpoint-name, text-transform) { + text-transform: map.get($token, $breakpoint-name, text-transform); } - @if map.has-key($token, $breakpoint-name, letter-spacing) { - letter-spacing: map.get($token, $breakpoint-name, letter-spacing); + @if map.has-key($token, $breakpoint-name, text-indent) { + text-indent: map.get($token, $breakpoint-name, text-indent); } } } diff --git a/packages/web/tests/scss.test.ts b/packages/web/tests/scss.test.ts new file mode 100644 index 0000000000..d6e0aec841 --- /dev/null +++ b/packages/web/tests/scss.test.ts @@ -0,0 +1,29 @@ +/** + * @jest-environment node + */ + +import { resolve } from 'path'; +import { runSass } from 'sass-true'; +import { sync } from 'glob'; +import { pathToFileURL } from 'url'; + +const importers = [ + // Make @tokens work + { + findFileUrl(url) { + if (!url.startsWith('@')) { + return null; + } + + return new URL( + pathToFileURL(resolve(process.cwd(), '../../node_modules/@lmc-eu/spirit-design-tokens/src/scss', url)), + ); + }, + }, +]; + +describe('Sass', () => { + const sassTestFiles = sync(resolve(process.cwd(), 'src/**/*.test.scss')); + + sassTestFiles.forEach((file) => runSass({ describe, it }, file, { importers })); +}); diff --git a/yarn.lock b/yarn.lock index 52b7e7dad0..ddf4424e6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6591,6 +6591,11 @@ dependencies: "@types/node" "*" +"@types/css@^0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/css/-/css-0.0.33.tgz#d0b49c4090c09c8e5dc01364560627e5ebb770f2" + integrity sha512-qjeDgh86R0LIeEM588q65yatc8Yyo/VvSIYFqq8JOIHDolhGNX0rz7k/OuxqDpnpqlefoHj8X4Ai/6hT9IWtKQ== + "@types/decompress@*": version "4.2.4" resolved "https://registry.yarnpkg.com/@types/decompress/-/decompress-4.2.4.tgz#dd2715d3ac1f566d03e6e302d1a26ffab59f8c5c" @@ -10375,6 +10380,15 @@ css.escape@^1.5.1: resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= +css@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== + dependencies: + inherits "^2.0.4" + source-map "^0.6.1" + source-map-resolve "^0.6.0" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -13789,6 +13803,17 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@10.3.10: + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@7.1.4: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" @@ -15554,6 +15579,15 @@ jackspeak@^2.0.3: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -15696,7 +15730,14 @@ jest-environment-jsdom@29.7.0: jest-util "^29.7.0" jsdom "^20.0.0" -jest-environment-node@^29.7.0: +jest-environment-node-single-context@29.1.0: + version "29.1.0" + resolved "https://registry.yarnpkg.com/jest-environment-node-single-context/-/jest-environment-node-single-context-29.1.0.tgz#cbf88724cfd3a00703a139240fd707887f5a1aea" + integrity sha512-sMQSq/b4aK9f0V1atn/ZfBgdrEQZzoZo95UzUTXaS/GtiCx9e62IFP4gX0GR+IGDu1k7UuqB/Xot46vJoOsoyg== + dependencies: + jest-environment-node "^29.5.0" + +jest-environment-node@^29.5.0, jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== @@ -21571,6 +21612,16 @@ sass-loader@13.3.2: dependencies: neo-async "^2.6.2" +sass-true@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/sass-true/-/sass-true-7.0.0.tgz#eabbc92f29b2d602a57234ec0fe65050fb0df930" + integrity sha512-sRdXX7MrrYdg+lPRm+/vIr8wVvDrNtWj3ttOVyIMHZQ8vNoV67+YjZKTsY9+B4Ecee+/U3ryXKJLi1YcMEkaJQ== + dependencies: + "@types/css" "^0.0.33" + css "^3.0.0" + jest-diff "^29.3.1" + lodash "^4.17.21" + sass@1.69.4: version "1.69.4" resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.4.tgz#10c735f55e3ea0b7742c6efa940bce30e07fbca2" @@ -22085,6 +22136,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"