import { assert } from "chai" ; import { suite , test } from "mocha-typescript" ; import { outdent } from "outdent" ; import { BlockCompiler , BlockDefinitionCompiler , INLINE_DEFINITION_FILE } from "../../src" ; import { REGEXP_COMMENT_DEFINITION_REF } from "../../src/PrecompiledDefinitions" ; import { setupImporting } from "../util/setupImporting" ; @ suite ( "Acceptance Test - Definition Files Full Workflow" ) export class AcceptanceTestDefinitionFilesFullWorkflow { @ test async "Process basic CSS Blocks source file and parse from definition file, using mock importer" ( ) { let { imports , config , factory , postcss } = setupImporting ( ) ; const blockDfnCompiler = new BlockDefinitionCompiler ( postcss , ( _b , p ) => p . replace ( ".block" , "" ) , config ) ; const compiler = new BlockCompiler ( postcss , config ) ; compiler . setDefinitionCompiler ( blockDfnCompiler ) ; imports . registerSource ( "/foo/bar/other.block.css" , `:scope { color: red; }` ) ; imports . registerSource ( "/foo/bar/source.block.css" , outdent `
        @block (default as other) from './other.block.css';
        @export (other);

        :scope { background-color: #FFF; }
        :scope[toggled] { color: #000; }
        :scope[foo="bar"] { border-top-color: #F00; }
        .item { width: 100px; }
        .item[toggled] { height: 100px; }
        .item[foo="bar"] { min-width: 100px; }
        .item + .item { border-left-color: #F00; }
        :scope[toggled] .item { border-right-color: #F00; }
        :scope[toggled] .item[foo="bar"] { border-bottom-color: #F00; }
      ` ) ; const originalBlock = await factory . getBlockFromPath ( "/foo/bar/source.block.css" ) ; const { css : cssTree , definition : definitionTree } = compiler . compileWithDefinition ( originalBlock , originalBlock . stylesheet ! , new Set ( ) , "/foo/bar/source.block.d.css" ) ; const compiledCss = cssTree . toString ( ) ; const definition = definitionTree . toString ( ) ; assert . equal ( outdent `
        ${ compiledCss }
      ` , outdent `
        /*#css-blocks ${ originalBlock . guid }*/
        .source { background-color: #FFF; }
        .source--toggled { color: #000; }
        .source--foo-bar { border-top-color: #F00; }
        .source__item { width: 100px; }
        .source__item--toggled { height: 100px; }
        .source__item--foo-bar { min-width: 100px; }
        .source__item + .source__item { border-left-color: #F00; }
        .source--toggled .source__item { border-right-color: #F00; }
        .source--toggled .source__item--foo-bar { border-bottom-color: #F00; }
        /*#blockDefinitionURL=/foo/bar/source.block.d.css*/
        /*#css-blocks end*/
      ` , "Compiled CSS contents match expected output" ) ; assert . equal ( outdent `
        ${ definition }
      ` , outdent `
        @block-syntax-version 1;
        @block other from "./other.css";
        @export (other);
        :scope {
            block-id: "${ originalBlock . guid }";
            block-name: "source";
            block-class: source
        }
        :scope[toggled] {
            block-class: source--toggled
        }
        :scope[foo="bar"] {
            block-class: source--foo-bar
        }
        .item {
            block-class: source__item
        }
        .item[toggled] {
            block-class: source__item--toggled
        }
        .item[foo="bar"] {
            block-class: source__item--foo-bar
        }
      ` , "Compiled definition contents match expected output" ) ; imports . reset ( ) ; factory . reset ( ) ; imports . registerSource ( "/foo/bar/other.css" , `:scope { color: red; }` ) ; imports . registerCompiledCssSource ( "/foo/bar/source.css" , compiledCss , "/foo/bar/source.block.d.css" , definition ) ; await factory . getBlockFromPath ( "/foo/bar/other.css" ) ; const reconstitutedBlock = await factory . getBlockFromPath ( "/foo/bar/source.css" ) ; assert . equal ( reconstitutedBlock . guid , originalBlock . guid , "GUIDs of original and reconstituted blocks match" ) ; assert . equal ( reconstitutedBlock . name , originalBlock . name , "Names of original and reconstituted blocks match" ) ; const expectedProperties = { "source" : { "::self" : [ "background-color" , ] , } , "source--toggled" : { "::self" : [ "color" , ] , } , "source--foo-bar" : { "::self" : [ "border-top-color" , ] , } , "source__item" : { "::self" : [ "width" , "border-left-color" , "border-right-color" , ] , } , "source__item--toggled" : { "::self" : [ "height" , ] , } , "source__item--foo-bar" : { "::self" : [ "min-width" , "border-bottom-color" , ] , } , } ; const expectedFoundClasses = Object . keys ( expectedProperties ) ; const foundClasses = reconstitutedBlock . presetClassesMap ( true ) ; assert . deepEqual ( Object . keys ( foundClasses ) , expectedFoundClasses , "Class nodes on reconstituted block matches expected list of classes" ) ; expectedFoundClasses . forEach ( expectedClass => { Object . keys ( expectedProperties [ expectedClass ] ) . forEach ( psuedo => { assert . deepEqual ( [ ... foundClasses [ expectedClass ] . rulesets . getProperties ( psuedo ) ] . sort ( ) , expectedProperties [ expectedClass ] [ psuedo ] . sort ( ) , `Properties on reconstituted class node ${ expectedClass }${ psuedo } match expected list` ) ; } ) ; } ) ; } @ test async "Process basic CSS Blocks source file and parse from embedded definitions, using mock importer" ( ) { let { imports , config , factory , postcss } = setupImporting ( ) ; const blockDfnCompiler = new BlockDefinitionCompiler ( postcss , ( _b , p ) => p . replace ( ".block" , "" ) , config ) ; const compiler = new BlockCompiler ( postcss , config ) ; compiler . setDefinitionCompiler ( blockDfnCompiler ) ; imports . registerSource ( "/foo/bar/source.block.css" , outdent `
        :scope { background-color: #FFF; }
        :scope[toggled] { color: #000; }
        :scope[foo="bar"] { border-top-color: #F00; }
        .item { width: 100px; }
        .item[toggled] { height: 100px; }
        .item[foo="bar"] { min-width: 100px; }
        .item + .item { border-left-color: #F00; }
        :scope[toggled] .item { border-right-color: #F00; }
        :scope[toggled] .item[foo="bar"] { border-bottom-color: #F00; }
      ` ) ; const originalBlock = await factory . getBlockFromPath ( "/foo/bar/source.block.css" ) ; const { css : cssTree } = compiler . compileWithDefinition ( originalBlock , originalBlock . stylesheet ! , new Set ( ) , INLINE_DEFINITION_FILE ) ; const compiledCss = cssTree . toString ( ) ; const embeddedDfnRegexResult = REGEXP_COMMENT_DEFINITION_REF . exec ( compiledCss ) ; if ( ! embeddedDfnRegexResult ) { assert . fail ( false , true , "Expected to find embedded definition data" ) ; return ; } const compiledCssNoDfn = compiledCss . replace ( embeddedDfnRegexResult [ 0 ] , "" ) ; assert . equal ( outdent `
        ${ compiledCssNoDfn }
      ` , outdent `
        /*#css-blocks ${ originalBlock . guid }*/
        .source { background-color: #FFF; }
        .source--toggled { color: #000; }
        .source--foo-bar { border-top-color: #F00; }
        .source__item { width: 100px; }
        .source__item--toggled { height: 100px; }
        .source__item--foo-bar { min-width: 100px; }
        .source__item + .source__item { border-left-color: #F00; }
        .source--toggled .source__item { border-right-color: #F00; }
        .source--toggled .source__item--foo-bar { border-bottom-color: #F00; }
        /*#css-blocks end*/
      ` , "Compiled CSS contents match expected output" ) ; assert . equal ( outdent `
        ${ Buffer . from ( embeddedDfnRegexResult [ 1 ] . split ( "," ) [ 1 ] , "base64" ) . toString ( "utf-8" ) }
      ` , outdent `
        @block-syntax-version 1;
        :scope {
            block-id: "${ originalBlock . guid }";
            block-name: "source";
            block-class: source
        }
        :scope[toggled] {
            block-class: source--toggled
        }
        :scope[foo="bar"] {
            block-class: source--foo-bar
        }
        .item {
            block-class: source__item
        }
        .item[toggled] {
            block-class: source__item--toggled
        }
        .item[foo="bar"] {
            block-class: source__item--foo-bar
        }
      ` , "Unencoded definition data matches expected output" ) ; imports . reset ( ) ; factory . reset ( ) ; imports . registerSource ( "/foo/bar/source.css" , compiledCss , undefined , true ) ; const reconstitutedBlock = await factory . getBlockFromPath ( "/foo/bar/source.css" ) ; assert . equal ( reconstitutedBlock . guid , originalBlock . guid , "GUIDs of original and reconstituted blocks match" ) ; assert . equal ( reconstitutedBlock . name , originalBlock . name , "Names of original and reconstituted blocks match" ) ; const expectedProperties = { "source" : { "::self" : [ "background-color" , ] , } , "source--toggled" : { "::self" : [ "color" , ] , } , "source--foo-bar" : { "::self" : [ "border-top-color" , ] , } , "source__item" : { "::self" : [ "width" , "border-left-color" , "border-right-color" , ] , } , "source__item--toggled" : { "::self" : [ "height" , ] , } , "source__item--foo-bar" : { "::self" : [ "min-width" , "border-bottom-color" , ] , } , } ; const expectedFoundClasses = Object . keys ( expectedProperties ) ; const foundClasses = reconstitutedBlock . presetClassesMap ( true ) ; assert . deepEqual ( Object . keys ( foundClasses ) , expectedFoundClasses , "Class nodes on reconstituted block matches expected list of classes" ) ; expectedFoundClasses . forEach ( expectedClass => { Object . keys ( expectedProperties [ expectedClass ] ) . forEach ( psuedo => { assert . deepEqual ( [ ... foundClasses [ expectedClass ] . rulesets . getProperties ( psuedo ) ] . sort ( ) , expectedProperties [ expectedClass ] [ psuedo ] . sort ( ) , `Properties on reconstituted class node ${ expectedClass }${ psuedo } match expected list` ) ; } ) ; } ) ; } }