test: add preset/css lockstep regression test
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Lockstep contract: for every preset in `PRESETS`, the compiled
|
||||
* `build/style-index.css` must contain a rule whose `filter:` declaration
|
||||
* includes every canonical `cssFilter` token in the same order.
|
||||
*
|
||||
* The source of truth for the canonical `cssFilter` per preset is
|
||||
* `src/presets.js`. The SCSS in `src/styles/style.scss` must mirror those
|
||||
* values using the intensity-scaling math (additive bias or direct
|
||||
* multiply). This test fails immediately if the two files ever drift.
|
||||
*
|
||||
* Tokenisation: split the cssFilter string on whitespace, then split each
|
||||
* token on `(` and `)` so the function name and argument are tracked
|
||||
* separately. The compiled CSS rule for the preset must contain a token
|
||||
* whose name+argument matches every canonical token, in order.
|
||||
*
|
||||
* The test reads from the build artifact rather than the SCSS source
|
||||
* because that is the CSS WordPress actually serves. If you change
|
||||
* style.scss, re-run `npm run build` to update build/style-index.css.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { PRESETS } from '../../src/presets';
|
||||
|
||||
const cssPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'build',
|
||||
'style-index.css'
|
||||
);
|
||||
|
||||
/**
|
||||
* Split a cssFilter string like
|
||||
* "saturate(1.5) sepia(0.6) brightness(1.08) contrast(1.1) hue-rotate(-8deg)"
|
||||
* into ordered tokens of the form { name, arg }.
|
||||
*/
|
||||
function tokenise( cssFilter ) {
|
||||
return cssFilter
|
||||
.trim()
|
||||
.split( /\s+/ )
|
||||
.map( ( token ) => {
|
||||
const [ , name, arg ] = token.match( /^([a-z-]+)\((.*)\)$/ );
|
||||
return { name, arg };
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull out the filter-declaration substring for a given preset from the
|
||||
* compiled CSS. Returns null if the rule is missing entirely.
|
||||
*/
|
||||
function filterDeclarationFor( css, slug ) {
|
||||
const selector = `.ksolo-image-filter-img.has-filter-${ slug }`;
|
||||
const ruleStart = css.indexOf( selector );
|
||||
if ( ruleStart === -1 ) {
|
||||
return null;
|
||||
}
|
||||
// Find the next "}" — that's the end of the rule body.
|
||||
const brace = css.indexOf( '{', ruleStart );
|
||||
const close = css.indexOf( '}', brace );
|
||||
if ( brace === -1 || close === -1 ) {
|
||||
return null;
|
||||
}
|
||||
const body = css.slice( brace, close );
|
||||
const filterIdx = body.indexOf( 'filter:' );
|
||||
if ( filterIdx === -1 ) {
|
||||
return null;
|
||||
}
|
||||
return body.slice( filterIdx + 'filter:'.length, close );
|
||||
}
|
||||
|
||||
describe( 'preset ↔ compiled CSS lockstep', () => {
|
||||
let css;
|
||||
|
||||
beforeAll( () => {
|
||||
css = fs.readFileSync( cssPath, 'utf8' );
|
||||
} );
|
||||
|
||||
test.each( PRESETS.map( ( p ) => [ p.slug, p ] ) )(
|
||||
'%s: every cssFilter token appears in the compiled CSS rule, in order',
|
||||
( slug, preset ) => {
|
||||
// 'normal' uses filter:none; there is no canonical cssFilter
|
||||
// token sequence to compare against. The base rule
|
||||
// `.wp-block-ksolo-image-filter img { filter: none; }` already
|
||||
// covers it; the existing styles.test.js checks that the
|
||||
// selector for 'normal' is absent from the per-preset
|
||||
// overrides. Skip the lockstep check here.
|
||||
if ( slug === 'normal' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decl = filterDeclarationFor( css, slug );
|
||||
expect( decl ).not.toBeNull();
|
||||
|
||||
const expected = tokenise( preset.cssFilter );
|
||||
expect( expected.length ).toBeGreaterThan( 0 );
|
||||
|
||||
// Walk the canonical tokens in order; for each, find the next
|
||||
// occurrence of `name(arg)` in the compiled declaration
|
||||
// (allowing for the calc() wrapper around the value).
|
||||
let cursor = 0;
|
||||
for ( const { name } of expected ) {
|
||||
const needle = `${ name }(`;
|
||||
const at = decl.indexOf( needle, cursor );
|
||||
expect( at ).toBeGreaterThanOrEqual( 0 );
|
||||
cursor = at + needle.length;
|
||||
}
|
||||
}
|
||||
);
|
||||
} );
|
||||
Reference in New Issue
Block a user