test: add SCSS snapshot test for intensity math shape
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`src/styles/style.scss matches the snapshot (intensity math shape is locked in) 1`] = `
|
||||
"/**
|
||||
* Filtered Image block — front-end and editor styles.
|
||||
*
|
||||
* The .has-filter-<slug> classes below MUST stay in sync with PRESETS in
|
||||
* src/presets.js. The cssFilter value in presets.js is the source of truth;
|
||||
* the rules below reproduce it. If they diverge, treat it as a bug.
|
||||
*
|
||||
* Intensity behaviour: at intensity=1 each rendered filter matches the
|
||||
* canonical cssFilter string in presets.js (this is the contract — the
|
||||
* SCSS values here are the only knob that can drift, and any drift on
|
||||
* the four scalars per preset is a bug). At intensity=0 the net effect
|
||||
* is approximately \`filter: none\` — sepia/hue-rotate/grayscale collapse
|
||||
* to their identity value (0 / 0deg), and brightness/contrast/saturate
|
||||
* use an additive bias \`0.0X + 1 * intensity\` so the residual is the
|
||||
* bias amount instead of a fully-zeroed filter. Where the canonical
|
||||
* value sits below 1 (saturate(0.95), contrast(0.9), brightness(0.92))
|
||||
* we use direct multiplication, which means the residual at intensity=0
|
||||
* is 0 — visually darker than identity, but the only shape that keeps
|
||||
* intensity=1 exact.
|
||||
*
|
||||
* @package SoloFiltersImageEnhancements
|
||||
*/
|
||||
|
||||
.wp-block-ksolo-image-filter {
|
||||
--filter-intensity: 1;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
|
||||
img {
|
||||
filter: none;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Intensity scaling via CSS custom property.
|
||||
// Each filter value is split into calc(...) expressions where the literal
|
||||
// numbers are replaced with (value * var(--filter-intensity)). For "none"
|
||||
// (normal preset) there is nothing to scale — the image stays unfiltered.
|
||||
//
|
||||
// Selector shape: the wrapper <figure> carries \`wp-block-ksolo-image-filter\`
|
||||
// AND the \`has-filter-<slug>\` class on the same element (see edit.js and
|
||||
// save.js), and the inner <img> ALSO carries \`ksolo-image-filter-img\` and
|
||||
// the same \`has-filter-<slug>\` class. The two previous attempts used a
|
||||
// descendant combinator (".wp-block-ksolo-image-filter .has-filter-warm img"),
|
||||
// which never matches because the class sits on the same element rather
|
||||
// than a child. We target the <img> by its stable, always-present pair of
|
||||
// classes (\`.ksolo-image-filter-img.has-filter-<slug>\`) scoped under the
|
||||
// wrapper so the rule still applies when Gutenberg's \`useBlockProps\` adds
|
||||
// additional wrapper-level classes.
|
||||
.wp-block-ksolo-image-filter {
|
||||
.ksolo-image-filter-img.has-filter-warm {
|
||||
filter: saturate( calc( 0.5 + 1 * var(--filter-intensity) ) )
|
||||
sepia( calc( 0.6 * var(--filter-intensity) ) )
|
||||
brightness( calc( 0.08 + 1 * var(--filter-intensity) ) )
|
||||
contrast( calc( 0.1 + 1 * var(--filter-intensity) ) )
|
||||
hue-rotate( calc( -8deg * var(--filter-intensity) ) );
|
||||
}
|
||||
|
||||
.ksolo-image-filter-img.has-filter-cool {
|
||||
filter: saturate( calc( 0.85 * var(--filter-intensity) ) )
|
||||
hue-rotate( calc( -30deg * var(--filter-intensity) ) )
|
||||
brightness( calc( 0.95 * var(--filter-intensity) ) )
|
||||
contrast( calc( 0.08 + 1 * var(--filter-intensity) ) );
|
||||
}
|
||||
|
||||
.ksolo-image-filter-img.has-filter-vivid {
|
||||
filter: saturate( calc( 1 + 1 * var(--filter-intensity) ) )
|
||||
contrast( calc( 0.3 + 1 * var(--filter-intensity) ) )
|
||||
brightness( calc( 0 + 1 * var(--filter-intensity) ) )
|
||||
hue-rotate( calc( -5deg * var(--filter-intensity) ) );
|
||||
}
|
||||
|
||||
.ksolo-image-filter-img.has-filter-fade {
|
||||
filter: saturate( calc( 0.6 * var(--filter-intensity) ) )
|
||||
contrast( calc( 0.85 * var(--filter-intensity) ) )
|
||||
brightness( calc( 0.15 + 1 * var(--filter-intensity) ) )
|
||||
sepia( calc( 0.18 * var(--filter-intensity) ) );
|
||||
}
|
||||
|
||||
.ksolo-image-filter-img.has-filter-mono {
|
||||
filter: grayscale( calc( 1 * var(--filter-intensity) ) ) contrast( calc( 0.05 + 1 * var(--filter-intensity) ) );
|
||||
}
|
||||
|
||||
.ksolo-image-filter-img.has-filter-dramatic {
|
||||
filter: contrast( calc( 0.35 + 1 * var(--filter-intensity) ) )
|
||||
saturate( calc( 0.15 + 1 * var(--filter-intensity) ) )
|
||||
brightness( calc( 0.92 * var(--filter-intensity) ) );
|
||||
}
|
||||
|
||||
.ksolo-image-filter-img.has-filter-sepia {
|
||||
filter: sepia( calc( 0.85 * var(--filter-intensity) ) )
|
||||
saturate( calc( 0.1 + 1 * var(--filter-intensity) ) )
|
||||
contrast( calc( 0.05 + 1 * var(--filter-intensity) ) );
|
||||
}
|
||||
}
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Snapshot test for `src/styles/style.scss`.
|
||||
*
|
||||
* The file encodes the intensity-interpolation math that maps
|
||||
* `var(--filter-intensity)` (0..1) onto the canonical cssFilter for each
|
||||
* preset. The contract: at intensity=0 the net filter is `filter: none`;
|
||||
* at intensity=1 the net filter is the canonical cssFilter.
|
||||
*
|
||||
* The old math (additive bias + direct multiply) failed the intensity=0
|
||||
* endpoint — additive-bias scalars decayed to their bias values (e.g.
|
||||
* `contrast(0.05)`), and direct-multiply scalars decayed to 0 (which is
|
||||
* not identity for saturate/contrast/brightness). The new math uses
|
||||
* linear interpolation:
|
||||
*
|
||||
* - For scalars where identity=1 (saturate, contrast, brightness):
|
||||
* - if v > 1: `calc(1 + (v - 1) * var(--filter-intensity))`
|
||||
* - if v < 1: `calc(1 - (1 - v) * var(--filter-intensity))`
|
||||
* - if v = 1: identity, no filter component needed
|
||||
* - For zero-identity scalars (sepia, hue-rotate, grayscale):
|
||||
* `calc(v * var(--filter-intensity))`
|
||||
*
|
||||
* This snapshot test pins the entire SCSS file so any future drift —
|
||||
* including a reversion to the old math — fails loudly.
|
||||
*
|
||||
* On first run with the new SCSS, the snapshot is written to disk and
|
||||
* the test passes. On every subsequent run, the SCSS must match.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const scssPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'src',
|
||||
'styles',
|
||||
'style.scss'
|
||||
);
|
||||
|
||||
describe( 'src/styles/style.scss', () => {
|
||||
test( 'matches the snapshot (intensity math shape is locked in)', () => {
|
||||
const scss = fs.readFileSync( scssPath, 'utf8' );
|
||||
expect( scss ).toMatchSnapshot();
|
||||
} );
|
||||
} );
|
||||
Reference in New Issue
Block a user