Skip to content

Commit

Permalink
Merge branch 'js-api-color-4' of github.com:oddbird/sass-spec into js…
Browse files Browse the repository at this point in the history
…-api-color-4
  • Loading branch information
jamesnw committed Nov 13, 2023
2 parents 64d225a + c66f99a commit c772c51
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 189 deletions.
3 changes: 2 additions & 1 deletion js-api-spec/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ const toLooselyEqual = (received: unknown, actual: number) => {
pass: Math.round((received * 10) ^ 5) === Math.round((actual * 10) ^ 5),
};
};

const toLooselyEqualColor = (received: unknown, actual: sass.SassColor) => {
function isSassColor(item: unknown): item is sass.SassColor {
return !!(item as sass.SassColor).assertColor();
Expand All @@ -367,7 +368,7 @@ const toLooselyEqualColor = (received: unknown, actual: sass.SassColor) => {
if (channel === null) {
if (actualChannel !== null) unequalIndices.push(index);
} else if (
!actualChannel ||
actualChannel === null ||
Math.round((channel * 10) ^ 5) !== Math.round((actualChannel * 10) ^ 5)
) {
unequalIndices.push(index);
Expand Down
264 changes: 134 additions & 130 deletions js-api-spec/value/color/color-4-channels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {List} from 'immutable';

import {spaces} from './spaces';
import {channelCases, channelNames} from './utils';
import {skipForImpl} from '../../utils';

const spaceNames = Object.keys(spaces) as KnownColorSpace[];

Expand Down Expand Up @@ -148,10 +149,12 @@ describe('Color 4 SassColor Channels', () => {
expect(colorWithAlpha(1).alpha).toBe(1);
expect(colorWithAlpha(0.5).alpha).toBe(0.5);
});

it('returns 1 if not set', () => {
const noAlphaColor = space.constructor(0, 0, 0);
expect(noAlphaColor.alpha).toBe(1);
});

it('returns 0 if missing', () => {
const noAlphaColor = space.constructor(0, 0, 0, null);
expect(noAlphaColor.alpha).toBe(0);
Expand All @@ -160,144 +163,145 @@ describe('Color 4 SassColor Channels', () => {

// TODO(sass#3654) Failing in dart-sass pending:
// https://github.com/sass/sass/issues/3654
describe('isChannelPowerless', () => {
function checkPowerless(
_color: SassColor,
powerless = [false, false, false]
) {
it(`channels ${_color.channels.toArray()} is ${powerless}`, () => {
expect(_color.isChannelPowerless(space.channels[0])).toBe(
powerless[0]
);
expect(_color.isChannelPowerless(space.channels[1])).toBe(
powerless[1]
);
expect(_color.isChannelPowerless(space.channels[2])).toBe(
powerless[2]
);
});
}

const [ch1, ch2, ch3] = space.ranges;
if (space.hasPowerless) {
// test powerless channels
switch (space.name) {
case 'hwb':
// If the combined `whiteness` and `blackness` values (after
// normalization) are equal to `100%`, then the `hue` channel is
// powerless.
checkPowerless(space.constructor(ch1[0], 0, 100), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[0], 100, 0), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[0], 50, 50), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 0, 100), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 100, 0), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 50, 50), [
true,
false,
false,
]);
skipForImpl('dart-sass', () => {
describe('isChannelPowerless', () => {
function checkPowerless(
_color: SassColor,
powerless = [false, false, false]
) {
it(`channels ${_color.channels.toArray()} is ${powerless}`, () => {
expect(_color.isChannelPowerless(space.channels[0])).toBe(
powerless[0]
);
expect(_color.isChannelPowerless(space.channels[1])).toBe(
powerless[1]
);
expect(_color.isChannelPowerless(space.channels[2])).toBe(
powerless[2]
);
});
}
const [ch1, ch2, ch3] = space.ranges;
if (space.hasPowerless) {
// test powerless channels
switch (space.name) {
case 'hwb':
// If the combined `whiteness` and `blackness` values (after
// normalization) are equal to `100%`, then the `hue` channel is
// powerless.
checkPowerless(space.constructor(ch1[0], 0, 100), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[0], 100, 0), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[0], 50, 50), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 0, 100), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 100, 0), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 50, 50), [
true,
false,
false,
]);

checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[0], ch3[0]));

break;
break;

case 'hsl':
// hsl- If the saturation of an HSL color is 0%, then the hue component is powerless.
checkPowerless(space.constructor(ch1[0], 0, ch3[0]), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[0], 0, ch3[1]), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[1]), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[0]), [
true,
false,
false,
]);
case 'hsl':
// hsl- If the saturation of an HSL color is 0%, then the hue component is powerless.
checkPowerless(space.constructor(ch1[0], 0, ch3[0]), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[0], 0, ch3[1]), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[1]), [
true,
false,
false,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[0]), [
true,
false,
false,
]);

checkPowerless(space.constructor(ch1[0], ch2[1], ch3[0]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[0]));
break;
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[0]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[0]));
break;

case 'lch':
case 'oklch':
// (ok)lch- If the `chroma` value is 0%, then the `hue` channel is powerless.
checkPowerless(space.constructor(ch1[0], 0, ch3[0]), [
false,
false,
true,
]);
checkPowerless(space.constructor(ch1[0], 0, ch3[1]), [
false,
false,
true,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[1]), [
false,
false,
true,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[0]), [
false,
false,
true,
]);
case 'lch':
case 'oklch':
// (ok)lch- If the `chroma` value is 0%, then the `hue` channel is powerless.
checkPowerless(space.constructor(ch1[0], 0, ch3[0]), [
false,
false,
true,
]);
checkPowerless(space.constructor(ch1[0], 0, ch3[1]), [
false,
false,
true,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[1]), [
false,
false,
true,
]);
checkPowerless(space.constructor(ch1[1], 0, ch3[0]), [
false,
false,
true,
]);

checkPowerless(space.constructor(ch1[0], ch2[1], ch3[0]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[0]));
break;
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[0]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[0]));
break;

default:
throw new Error(
`Unimplemented isPowerless check for ${space.name}`
);
default:
throw new Error(
`Unimplemented isPowerless check for ${space.name}`
);
}
} else {
checkPowerless(space.constructor(ch1[0], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[0], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[0], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[0]));
}
} else {
checkPowerless(space.constructor(ch1[0], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[0], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[0]));
checkPowerless(space.constructor(ch1[1], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[0], ch3[1]));
checkPowerless(space.constructor(ch1[1], ch2[0], ch3[1]));
checkPowerless(space.constructor(ch1[0], ch2[1], ch3[0]));
}
});
});
});
});
Expand Down
36 changes: 21 additions & 15 deletions js-api-spec/value/color/color-4-conversions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,20 @@ describe('Color 4 SassColors Conversions', () => {
// TODO: Waiting on new ColorJS release to fix `hue` interpolation:
// https://github.com/LeaVerou/color.js/pull/338
skipForImpl('sass-embedded', () => {
describe('interpolate', () => {
it('interpolates examples', () => {
const examples = interpolations[space.name];
examples.forEach(([input, output]) => {
const res = color.interpolate(blue, {
weight: input.weight,
method: input.method as HueInterpolationMethod,
// TODO: Failing in dart-sass because `hue` should be normalized to the
// [0, 360] range
skipForImpl('dart-sass', () => {
describe('interpolate', () => {
it('interpolates examples', () => {
const examples = interpolations[space.name];
examples.forEach(([input, output]) => {
const res = color.interpolate(blue, {
weight: input.weight,
method: input.method as HueInterpolationMethod,
});
const outputColor = space.constructor(...output);
expect(res).toLooselyEqualColor(outputColor);
});
const outputColor = space.constructor(...output);
expect(res).toLooselyEqualColor(outputColor);
});
});
});
Expand Down Expand Up @@ -155,12 +159,14 @@ describe('Color 4 SassColors Conversions', () => {
expect(() => color.change({lightness: -1})).toThrow();
});
// TODO: Failing for oklab and oklch in dart-sass
it('throws on lightness higher than bounds', () => {
const index = space.channels.findIndex(
channel => channel === 'lightness'
);
const lightness = space.ranges[index][1] + 1;
expect(() => color.change({lightness})).toThrow();
skipForImpl('dart-sass', () => {
it('throws on lightness higher than bounds', () => {
const index = space.channels.findIndex(
channel => channel === 'lightness'
);
const lightness = space.ranges[index][1] + 1;
expect(() => color.change({lightness})).toThrow();
});
});
});
}
Expand Down
17 changes: 10 additions & 7 deletions js-api-spec/value/color/color-4-spaces.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {Value, SassColor} from 'sass';
import type {KnownColorSpace} from 'sass';

import {spaces} from './spaces';
import {skipForImpl} from '../../utils';

const spaceNames = Object.keys(spaces) as KnownColorSpace[];

Expand Down Expand Up @@ -63,13 +64,15 @@ describe('Color 4 SassColors Spaces', () => {
});

// TODO: Failing for oklab and oklch in dart-sass
it('throws on lightness higher than bounds', () => {
const index = space.channels.findIndex(
channel => channel === 'lightness'
);
const channels = [...space.pink] as [number, number, number];
channels[index] = space.ranges[index][1] + 1;
expect(() => space.constructor(...channels)).toThrow();
skipForImpl('dart-sass', () => {
it('throws on lightness higher than bounds', () => {
const index = space.channels.findIndex(
channel => channel === 'lightness'
);
const channels = [...space.pink] as [number, number, number];
channels[index] = space.ranges[index][1] + 1;
expect(() => space.constructor(...channels)).toThrow();
});
});
});
}
Expand Down
Loading

0 comments on commit c772c51

Please sign in to comment.