Skip to content

Commit

Permalink
Merge pull request #733 from ckeditor/ck/9668
Browse files Browse the repository at this point in the history
Feature (tests): Created the `equalMarkup` and `attribute` chai assertions. They are loaded automatically when running tests. Closes ckeditor/ckeditor5#9668.
  • Loading branch information
pomek authored Nov 24, 2021
2 parents b36893b + 2f1fde5 commit e219608
Show file tree
Hide file tree
Showing 8 changed files with 616 additions and 95 deletions.
17 changes: 17 additions & 0 deletions packages/ckeditor5-dev-tests/lib/tasks/runautomatedtests.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ function createEntryFile( globPatterns, production ) {
const entryFileContent = allFiles
.map( file => 'import "' + file + '";' );

// Inject the custom chai assertions. See ckeditor/ckeditor5#9668.
const assertionsDir = path.join( __dirname, '..', 'utils', 'automated-tests', 'assertions' ).replace( /\\/g, '/' );
const customAssertions = fs.readdirSync( assertionsDir ).map( assertionFileName => {
return [
assertionFileName,
path.parse( assertionFileName ).name.replace( /-([a-z])/g, value => value[ 1 ].toUpperCase() )
];
} );

// Two loops are needed to achieve correct order in `ckeditor5/build/.automated-tests/entry-point.js`.
for ( const [ fileName, functionName ] of customAssertions ) {
entryFileContent.push( `import ${ functionName }Factory from "${ assertionsDir }/${ fileName }";` );
}
for ( const [ , functionName ] of customAssertions ) {
entryFileContent.push( `${ functionName }Factory( chai );` );
}

if ( production ) {
entryFileContent.unshift( `
const originalWarn = console.warn;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* Factory function that registers the `attribute` assertion.
*
* @param {Chai} chai
*/
module.exports = chai => {
/**
* Asserts that the target has an attribute with the given key name.
*
* expect( selection ).to.have.attribute( 'linkHref' );
*
* When `value` is provided, `.attribute` also asserts that the attribute's value is equal to the given `value`.
*
* expect( selection ).to.have.attribute( 'linkHref', 'example.com' );
*
* Negations works as well.
*
* @param {String} key Key of attribute to assert.
* @param {String} [value] Attribute value to assert.
*/
chai.Assertion.addMethod( 'attribute', function attributeAssertion( key, value ) {
const obj = this._obj;

if ( arguments.length === 1 ) {
// Check if it has the method at all.
new chai.Assertion( obj ).to.respondTo( 'hasAttribute' );

// Check if it has the attribute.
const hasAttribute = obj.hasAttribute( key );
this.assert(
hasAttribute === true,
`expected #{this} to have attribute '${ key }'`,
`expected #{this} to not have attribute '${ key }'`,
!chai.util.flag( this, 'negate' ),
hasAttribute
);
}

// If a value was given.
if ( arguments.length >= 2 ) {
// Check if it has the method at all.
new chai.Assertion( obj ).to.respondTo( 'getAttribute' );

const attributeValue = obj.getAttribute( key );
this.assert(
attributeValue === value,
`expected #{this} to have attribute '${ key }' of #{exp}, but got #{act}`,
`expected #{this} to not have attribute '${ key }' of #{exp}`,
value,
attributeValue
);
}
} );
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

const AssertionError = require( 'assertion-error' );
const { html_beautify: beautify } = require( 'js-beautify/js/lib/beautify-html' );

/**
* Factory function that registers the `equalMarkup` assertion.
*
* @param {Chai} chai
*/
module.exports = chai => {
/**
* Custom assertion that tests whether two given strings containing markup language are equal.
* Unlike `expect().to.equal()` form Chai assertion library, this assertion formats the markup before showing a diff.
*
* This assertion can be used to test HTML strings and string containing serialized model.
*
* // Will throw an assertion error.
* expect(
* '<paragraph>foo bXXX[]r baz</paragraph>'
* ).to.equalMarkup(
* '<paragraph>foo bYYY[]r baz</paragraph>'
* );
*
* @param {String} expected Markup to compare.
*/
chai.Assertion.addMethod( 'equalMarkup', function( expected ) {
const actual = this._obj;
const message = 'Expected markup strings to be equal';

if ( actual !== expected ) {
throw new AssertionError( message, {
actual: formatMarkup( actual ),
expected: formatMarkup( expected ),
showDiff: true
} );
}
} );
};

// Renames the $text occurrences as it is not properly formatted by the beautifier - it is tread as a block.
const TEXT_TAG_PLACEHOLDER = 'span data-cke="true"';
const TEXT_TAG_PLACEHOLDER_REGEXP = new RegExp( TEXT_TAG_PLACEHOLDER, 'g' );

function formatMarkup( string ) {
const htmlSafeString = string.replace( /\$text/g, TEXT_TAG_PLACEHOLDER );

const beautifiedMarkup = beautify( htmlSafeString, {
indent_size: 2,
space_in_empty_paren: true
} );

return beautifiedMarkup.replace( TEXT_TAG_PLACEHOLDER_REGEXP, '$text' );
}
2 changes: 2 additions & 0 deletions packages/ckeditor5-dev-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@ckeditor/ckeditor5-dev-utils": "^26.0.1",
"@ckeditor/ckeditor5-dev-webpack-plugin": "^26.0.1",
"@ckeditor/ckeditor5-inspector": "^2.2.2",
"assertion-error": "^1.1.0",
"babel-core": "^6.26.3",
"babel-loader": "^8.1.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
Expand All @@ -23,6 +24,7 @@
"fs-extra": "^9.0.0",
"glob": "^7.1.6",
"istanbul-instrumenter-loader": "^3.0.1",
"js-beautify": "^1.11.0",
"karma": "^5.0.9",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.0",
Expand Down
112 changes: 111 additions & 1 deletion packages/ckeditor5-dev-tests/tests/tasks/runautomatedtests.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const sinon = require( 'sinon' );
const proxyquire = require( 'proxyquire' );
const expect = require( 'chai' ).expect;
const chalk = require( 'chalk' );
const path = require( 'path' );

describe( 'runAutomatedTests', () => {
let sandbox, stubs, runAutomatedTests, karmaServerCallback;
Expand Down Expand Up @@ -44,7 +45,8 @@ beforeEach( () => {
stubs = {
fs: {
writeFileSync: sandbox.stub(),
utimesSync: sandbox.stub()
utimesSync: sandbox.stub(),
readdirSync: sandbox.stub()
},
log: {
info: sandbox.stub(),
Expand Down Expand Up @@ -111,6 +113,8 @@ beforeEach( () => {
production: true
};

stubs.fs.readdirSync.returns( [] );

stubs.transformFileOptionToTestGlob.returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/**/*.js',
'/workspace/packages/ckeditor-basic-styles/tests/**/*.js'
Expand Down Expand Up @@ -167,6 +171,8 @@ beforeEach( () => {
production: true
};

stubs.fs.readdirSync.returns( [] );

stubs.transformFileOptionToTestGlob.onFirstCall().returns( [
'/workspace/packages/ckeditor5-basic-foo/tests/**/*.js',
'/workspace/packages/ckeditor-basic-foo/tests/**/*.js'
Expand Down Expand Up @@ -206,6 +212,8 @@ beforeEach( () => {

const consoleWarnStub = sandbox.stub( console, 'warn' );

stubs.fs.readdirSync.returns( [] );

stubs.transformFileOptionToTestGlob.returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/**/*.js',
'/workspace/packages/ckeditor-basic-styles/tests/**/*.js'
Expand Down Expand Up @@ -242,6 +250,8 @@ beforeEach( () => {

sandbox.stub( console, 'warn' );

stubs.fs.readdirSync.returns( [] );

stubs.transformFileOptionToTestGlob.returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/**/*.js',
'/workspace/packages/ckeditor-basic-styles/tests/**/*.js'
Expand Down Expand Up @@ -272,6 +282,8 @@ beforeEach( () => {
production: true
};

stubs.fs.readdirSync.returns( [] );

stubs.transformFileOptionToTestGlob.returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/**/*.js',
'/workspace/packages/ckeditor-basic-styles/tests/**/*.js'
Expand All @@ -293,4 +305,102 @@ beforeEach( () => {
expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( codeMakingConsoleUseThrowErrors );
} );
} );

it( 'should load custom assertions automatically (camelCase)', done => {
const options = {
files: [
'basic-styles'
],
production: true
};

stubs.fs.readdirSync.returns( [ 'assertionA.js', 'assertionB.js' ] );

stubs.transformFileOptionToTestGlob.returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/**/*.js',
'/workspace/packages/ckeditor-basic-styles/tests/**/*.js'
] );

stubs.glob.sync.onFirstCall().returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/bold.js',
'/workspace/packages/ckeditor5-basic-styles/tests/italic.js'
] );

stubs.glob.sync.onSecondCall().returns( [] );

const assertionsDir = path.join( __dirname, '..', '..', 'lib', 'utils', 'automated-tests', 'assertions' ).replace( /\\/g, '/' );

const expectedEntryPointContent = [
`import assertionAFactory from "${ assertionsDir }/assertionA.js";`,
`import assertionBFactory from "${ assertionsDir }/assertionB.js";`,
'assertionAFactory( chai );',
'assertionBFactory( chai );',
''
].join( '\n' );

setTimeout( () => {
karmaServerCallback( 0 );
} );

runAutomatedTests( options )
.then( () => {
expect( stubs.mkdirp.sync.calledOnce ).to.equal( true );
expect( stubs.mkdirp.sync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests' );

expect( stubs.fs.writeFileSync.calledOnce ).to.equal( true );
expect( stubs.fs.writeFileSync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests/entry-point.js' );
expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( expectedEntryPointContent );

done();
} );
} );

it( 'should load custom assertions automatically (kebab-case)', done => {
const options = {
files: [
'basic-styles'
],
production: true
};

stubs.fs.readdirSync.returns( [ 'assertion-a.js', 'assertion-b.js' ] );

stubs.transformFileOptionToTestGlob.returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/**/*.js',
'/workspace/packages/ckeditor-basic-styles/tests/**/*.js'
] );

stubs.glob.sync.onFirstCall().returns( [
'/workspace/packages/ckeditor5-basic-styles/tests/bold.js',
'/workspace/packages/ckeditor5-basic-styles/tests/italic.js'
] );

stubs.glob.sync.onSecondCall().returns( [] );

const assertionsDir = path.join( __dirname, '..', '..', 'lib', 'utils', 'automated-tests', 'assertions' ).replace( /\\/g, '/' );

const expectedEntryPointContent = [
`import assertionAFactory from "${ assertionsDir }/assertion-a.js";`,
`import assertionBFactory from "${ assertionsDir }/assertion-b.js";`,
'assertionAFactory( chai );',
'assertionBFactory( chai );',
''
].join( '\n' );

setTimeout( () => {
karmaServerCallback( 0 );
} );

runAutomatedTests( options )
.then( () => {
expect( stubs.mkdirp.sync.calledOnce ).to.equal( true );
expect( stubs.mkdirp.sync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests' );

expect( stubs.fs.writeFileSync.calledOnce ).to.equal( true );
expect( stubs.fs.writeFileSync.firstCall.args[ 0 ] ).to.equal( '/workspace/build/.automated-tests/entry-point.js' );
expect( stubs.fs.writeFileSync.firstCall.args[ 1 ] ).to.include( expectedEntryPointContent );

done();
} );
} );
} );
Loading

0 comments on commit e219608

Please sign in to comment.