From 6b312ad8bc22127084f42bddb444fc5d5218afb5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 23 Sep 2024 17:22:13 -0700 Subject: [PATCH 1/2] Add tests for parsing missing channels from Sass --- js-api-spec/utils.ts | 36 +++++++++++++++++-- .../value/color/color-4-channels.test.ts | 20 +++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/js-api-spec/utils.ts b/js-api-spec/utils.ts index 29dabfa23..1e8ba69c2 100644 --- a/js-api-spec/utils.ts +++ b/js-api-spec/utils.ts @@ -4,13 +4,15 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import {info} from 'sass'; +import * as sass from 'sass'; /* Whether the tests are running in a browser context. */ export const isBrowser = !global.process; /** The name of the implementation of Sass being tested. */ -export const sassImpl = info.split('\t')[0] as 'dart-sass' | 'sass-embedded'; +export const sassImpl = sass.info.split('\t')[0] as + | 'dart-sass' + | 'sass-embedded'; type Implementation = 'dart-sass' | 'sass-embedded' | 'browser'; @@ -116,3 +118,33 @@ export async function captureStdioAsync( return {out, err}; } + +/** + * Parses {@link expression} as a Sass expression, evaluates it, and returns its + * value. The expression has access to all the built-in modules at their usual + * URLs. + */ +export function evaluateExpression(expression: string): sass.Value { + let value: sass.Value | undefined; + sass.compileString( + ` + @use "sass:color"; + @use "sass:list"; + @use "sass:map"; + @use "sass:math"; + @use "sass:meta"; + @use "sass:selector"; + @use "sass:string"; + $_: fn((${expression})); + `, + { + functions: { + 'fn($arg)': args => { + value = args[0]; + return sass.sassNull; + }, + }, + } + ); + return value!; +} diff --git a/js-api-spec/value/color/color-4-channels.test.ts b/js-api-spec/value/color/color-4-channels.test.ts index 9bcdee6b9..780a2e831 100644 --- a/js-api-spec/value/color/color-4-channels.test.ts +++ b/js-api-spec/value/color/color-4-channels.test.ts @@ -10,6 +10,7 @@ import {List} from 'immutable'; import {spaces} from './spaces'; import {channelCases, channelNames} from './utils'; +import {evaluateExpression} from '../../utils'; const spaceNames = Object.keys(spaces) as KnownColorSpace[]; @@ -311,6 +312,25 @@ describe('Color 4 SassColor Channels', () => { ); }); }); + + describe('parsed from Sass code', () => { + it('parses defined values', () => { + const color = evaluateExpression( + `color(display-p3 ${spaces['display-p3'].pink.join(' ')} / 0.5)` + ) as SassColor; + expect(color.channels).toFuzzyEqualList(spaces['display-p3'].pink); + expect(color.alpha).toFuzzyEqual(0.5); + }); + + // Regression test for sass/sass#3950 + it('parses missing values', () => { + const color = evaluateExpression( + 'color(display-p3 0 none 1 / none)' + ) as SassColor; + expect(color.channelsOrNull).toFuzzyEqualList([0, null, 1]); + expect(color.isChannelMissing('alpha')).toBeTrue(); + }); + }); }); /** From f02656c4367faf48f2d5239e6be3a7799dc71bff Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 23 Sep 2024 17:45:04 -0700 Subject: [PATCH 2/2] Add tests for serializing missing channels to Sass --- js-api-spec/utils.ts | 26 +++++++++++------ .../value/color/color-4-channels.test.ts | 29 ++++++++++++++++++- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/js-api-spec/utils.ts b/js-api-spec/utils.ts index 1e8ba69c2..3a464a87c 100644 --- a/js-api-spec/utils.ts +++ b/js-api-spec/utils.ts @@ -128,15 +128,15 @@ export function evaluateExpression(expression: string): sass.Value { let value: sass.Value | undefined; sass.compileString( ` - @use "sass:color"; - @use "sass:list"; - @use "sass:map"; - @use "sass:math"; - @use "sass:meta"; - @use "sass:selector"; - @use "sass:string"; - $_: fn((${expression})); - `, + @use "sass:color"; + @use "sass:list"; + @use "sass:map"; + @use "sass:math"; + @use "sass:meta"; + @use "sass:selector"; + @use "sass:string"; + $_: fn((${expression})); + `, { functions: { 'fn($arg)': args => { @@ -148,3 +148,11 @@ export function evaluateExpression(expression: string): sass.Value { ); return value!; } + +/** Converts {@link value} to its serialized Sass representation. */ +export function serializeValue(value: sass.Value): string { + const result = sass.compileString('a {b: fn()}', { + functions: {'fn()': () => value}, + }); + return result.css.match(/b: ?(.*);/)![1]; +} diff --git a/js-api-spec/value/color/color-4-channels.test.ts b/js-api-spec/value/color/color-4-channels.test.ts index 780a2e831..4c27acd87 100644 --- a/js-api-spec/value/color/color-4-channels.test.ts +++ b/js-api-spec/value/color/color-4-channels.test.ts @@ -10,7 +10,7 @@ import {List} from 'immutable'; import {spaces} from './spaces'; import {channelCases, channelNames} from './utils'; -import {evaluateExpression} from '../../utils'; +import {evaluateExpression, serializeValue} from '../../utils'; const spaceNames = Object.keys(spaces) as KnownColorSpace[]; @@ -331,6 +331,33 @@ describe('Color 4 SassColor Channels', () => { expect(color.isChannelMissing('alpha')).toBeTrue(); }); }); + + describe('serialized to CSS', () => { + it('with defined values', () => + expect( + serializeValue( + spaces['display-p3'] + .constructor(...spaces['display-p3'].pink) + .change({alpha: 0.5}) + ) + ).toEqual( + 'color(display-p3 0.9510333334 0.6749909746 0.7568568354 / 0.5)' + )); + + // Regression test for sass/sass#3950 + it('with missing values', () => + expect( + serializeValue( + new SassColor({ + red: 0, + green: null, + blue: 1, + alpha: null, + space: 'display-p3', + }) + ) + ).toEqual('color(display-p3 0 none 1 / none)')); + }); }); /**