Skip to content
This repository has been archived by the owner on Aug 24, 2022. It is now read-only.

Add test module grouping by default #243

Merged
merged 7 commits into from
May 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 63 additions & 14 deletions broccoli-template-linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ const Filter = require('broccoli-persistent-filter');
const md5Hex = require('md5-hex');
const stringify = require('json-stable-stringify');
const chalk = require('chalk');
const jsStringEscape = require('js-string-escape');
const Linter = require('ember-template-lint');
const debug = require('debug')('template-lint:broccoli');
const projectLocalizationAddon = require('./lib/utils/project-localization-framework');
const testGenerators = require('aot-test-generators');
const testGeneratorNames = Object.keys(testGenerators);
const concat = require('broccoli-concat');

function TemplateLinter(inputNode, _options) {
if (!(this instanceof TemplateLinter)) { return new TemplateLinter(inputNode, _options); }
Expand All @@ -28,9 +30,16 @@ function TemplateLinter(inputNode, _options) {
this.options = options;
this._console = this.options.console || console;
this._templatercConfig = undefined;
this._generateTestFile = this.options.generateTestFile || function() {
return '';
};

if (this.options.testGenerator) {
let testGenerator = testGenerators[this.options.testGenerator];
if (!testGenerator) {
throw new Error(`No test generator found for "testGenerator: ${this.options.testGenerator}"`);
}

this._testGenerator = testGenerator;
}

this.linter = new Linter(options);

debug('Linter config: %s', JSON.stringify(this.linter.config));
Expand All @@ -51,7 +60,8 @@ TemplateLinter.prototype.baseDir = function() {
TemplateLinter.prototype.cacheKeyProcessString = function(string, relativePath) {
return md5Hex([
stringify(this.linter.config),
this._generateTestFile.toString(),
this.options.testGenerator || '',
this.options.groupName || '',
string,
relativePath
]);
Expand Down Expand Up @@ -101,15 +111,21 @@ TemplateLinter.prototype.processString = function(contents, relativePath) {
}, this)
.join('\n');


let output = this._generateTestFile(
'TemplateLint - ' + relativePath,
[{
name: 'should pass TemplateLint',
passed: passed,
errorMessage: jsStringEscape(relativePath + ' should pass TemplateLint.\n' + errorDisplay)
}]
);
let output = '';
if (this._testGenerator) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we properly support this anyways? If you have a project today without ember-cli-qunit or ember-cli-mocha (prior to this PR) what happens? What happens after this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a project today without ember-cli-qunit or ember-cli-mocha (prior to this PR) what happens?

Ember CLI will display a warning that you should install a test framework (for every generated test 😞) and it will generate an empty file

What happens after this change?

ember-cli-template-lint will display a warning that it couldn't detect a test framework (once) and it will generate an empty file

if (this.options.groupName) {
output = this._testGenerator.test(relativePath, passed,
`${relativePath} should pass TemplateLint.\n\n${errorDisplay}`);

} else {
output = [
this._testGenerator.suiteHeader(`TemplateLint | ${relativePath}`),
this._testGenerator.test('should pass TemplateLint', passed,
`${relativePath} should pass TemplateLint.\n\n${errorDisplay}`),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the errorDisplay is escaped by aot-test-generators?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, aot-test-generators handles all escaping now, and actually with far less bugs than js-escape-string did before...

this._testGenerator.suiteFooter()
].join('');
}
}

debug('Found %s errors for %s with \ncontents: \n%s\nerrors: \n%s', errors.length, relativePath, contents, errorDisplay);

Expand Down Expand Up @@ -149,4 +165,37 @@ TemplateLinter.prototype.issueLocalizationWarningIfNeeded = function() {
}
};

TemplateLinter.create = function(inputNode, options) {
options = options || {};

if (!options.groupName) {
return new TemplateLinter(inputNode, options);
}

if (testGeneratorNames.indexOf(options.testGenerator) === -1) {
throw new Error(`The "groupName" options can only be used with a "testGenerator" option of: ${testGeneratorNames}`);
}

let testGenerator = testGenerators[options.testGenerator];

let headerName = 'TemplateLint';
if (options.groupName !== 'templates') {
headerName += ` | ${options.groupName}`;
}

let header = testGenerator.suiteHeader(headerName);
let footer = testGenerator.suiteFooter();

let lint = new TemplateLinter(inputNode, options);

return concat(lint, {
outputFile: `/${options.groupName}.template.lint-test.js`,
header,
inputFiles: ['**/*.template.lint-test.js'],
footer,
sourceMapConfig: { enabled: false },
allowNone: true
});
};

module.exports = TemplateLinter;
26 changes: 24 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ const PrintFailing = require('./lib/commands/print-failing');
module.exports = {
name: 'ember-cli-template-lint',

included: function (app) {
this._super.included.apply(this, arguments);
this._options = app.options['ember-cli-template-lint'] || {};

if (!('testGenerator' in this._options)) {
let VersionChecker = require('ember-cli-version-checker');
let checker = new VersionChecker(this);

if (checker.for('ember-cli-qunit', 'npm').satisfies('*')) {
this._options.testGenerator = 'qunit';
} else if (checker.for('ember-cli-mocha', 'npm').satisfies('*')) {
this._options.testGenerator = 'mocha';
} else {
this.ui.warn(
'[ember-cli-template-lint] Test framework detection was unsuccessful. ' +
'Please provide a "testGenerator" option explicitly to enable the test generators.'
);
}
}
},

includedCommands() {
return {
'template-lint:print-failing': PrintFailing
Expand All @@ -31,10 +52,11 @@ module.exports = {
}
};

return new TemplateLinter(tree, {
return TemplateLinter.create(tree, {
annotation: 'TemplateLinter',
templatercPath: this.project.root + '/.template-lintrc',
generateTestFile: this.project.generateTestFile,
testGenerator: this._options.testGenerator,
groupName: (this._options.group !== false) ? type : undefined,
console: mockConsole,
project: this.project
});
Expand Down
162 changes: 138 additions & 24 deletions node-tests/acceptance/broccoli-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@ describe('broccoli-template-linter', function() {
};
}

it('uses provided generateTestFile to return a test file', co.wrap(function *() {
it('generates a QUnit test file if "testGenerator: qunit" is provided', co.wrap(function *() {
input.copy(`${fixturePath}/with-errors`);

subject = new TemplateLinter(`${input.path()}/app`, {
subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
generateTestFile(moduleName, tests) {
return tests[0].errorMessage;
}
testGenerator: 'qunit'
});

output = createBuilder(subject);
Expand All @@ -52,15 +50,137 @@ describe('broccoli-template-linter', function() {
expect(result.templates).to.have.property('application.template.lint-test.js');

let contents = result.templates['application.template.lint-test.js'];
expect(contents).to.contain('Incorrect indentation for `div`');
expect(contents).to.contain('Incorrect indentation for `p`');
expect(contents).to.contain('HTML comment detected');
expect(contents).to.equal([
'QUnit.module(\'TemplateLint | templates/application.hbs\');',
'QUnit.test(\'should pass TemplateLint\', function(assert) {',
' assert.expect(1);',
(
' assert.ok(false, \'templates/application.hbs should pass TemplateLint.\\n\\n' +
'block-indentation: Incorrect indentation for `div` beginning at L2:C0. Expected `</div>` ending at L5:C9 to be at an indentation of 0 but was found at 3. (templates/application @ L5:C9): \\n' +
'`<div>\\n <p>\\n </p>\\n </div>`\\n' +
'block-indentation: Incorrect indentation for `p` beginning at L3:C2. Expected `</p>` ending at L4:C5 to be at an indentation of 2 but was found at 1. (templates/application @ L4:C5): \\n' +
'`<p>\\n </p>`\\n' +
'html-comments: HTML comment detected (templates/application): \\n' +
'`<!-- silly html comments -->`\');'
),
`});\n`
].join('\n'));
}));

it('generates a Mocha test file if "testGenerator: mocha" is provided', co.wrap(function *() {
input.copy(`${fixturePath}/with-errors`);

subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
testGenerator: 'mocha'
});

output = createBuilder(subject);
yield output.build();

let result = output.read();
expect(result).to.have.property('templates');
expect(result.templates).to.have.property('application.template.lint-test.js');

let contents = result.templates['application.template.lint-test.js'];
expect(contents).to.equal([
'describe(\'TemplateLint | templates/application.hbs\', function() {',
' it(\'should pass TemplateLint\', function() {',
' // test failed',
(
' var error = new chai.AssertionError(\'templates/application.hbs should pass TemplateLint.\\n\\n' +
'block-indentation: Incorrect indentation for `div` beginning at L2:C0. Expected `</div>` ending at L5:C9 to be at an indentation of 0 but was found at 3. (templates/application @ L5:C9): \\n' +
'`<div>\\n <p>\\n </p>\\n </div>`\\n' +
'block-indentation: Incorrect indentation for `p` beginning at L3:C2. Expected `</p>` ending at L4:C5 to be at an indentation of 2 but was found at 1. (templates/application @ L4:C5): \\n' +
'`<p>\\n </p>`\\n' +
'html-comments: HTML comment detected (templates/application): \\n' +
'`<!-- silly html comments -->`\');'
),
' error.stack = undefined;',
' throw error;',
' });',
'});\n'
].join('\n'));
}));

it('generates a QUnit test file if "testGenerator: qunit" and "groupName: foo" are provided', co.wrap(function *() {
this.timeout(10000);

input.copy(`${fixturePath}/with-errors`);

subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
testGenerator: 'qunit',
groupName: 'foo'
});

output = createBuilder(subject);
yield output.build();

let result = output.read();
expect(result).to.have.property('foo.template.lint-test.js');

let contents = result['foo.template.lint-test.js'];
expect(contents.trim()).to.equal([
'QUnit.module(\'TemplateLint | foo\');',
'',
'QUnit.test(\'templates/application.hbs\', function(assert) {',
' assert.expect(1);',
(
' assert.ok(false, \'templates/application.hbs should pass TemplateLint.\\n\\n' +
'block-indentation: Incorrect indentation for `div` beginning at L2:C0. Expected `</div>` ending at L5:C9 to be at an indentation of 0 but was found at 3. (templates/application @ L5:C9): \\n' +
'`<div>\\n <p>\\n </p>\\n </div>`\\n' +
'block-indentation: Incorrect indentation for `p` beginning at L3:C2. Expected `</p>` ending at L4:C5 to be at an indentation of 2 but was found at 1. (templates/application @ L4:C5): \\n' +
'`<p>\\n </p>`\\n' +
'html-comments: HTML comment detected (templates/application): \\n' +
'`<!-- silly html comments -->`\');'
),
'});'
].join('\n'));
}));

it('returns an empty string if no generateTestFile is provided', co.wrap(function *() {
it('generates a Mocha test file if "testGenerator: mocha" and "groupName: foo" are provided', co.wrap(function *() {
input.copy(`${fixturePath}/with-errors`);

subject = new TemplateLinter(`${input.path()}/app`, {
subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
testGenerator: 'mocha',
groupName: 'foo'
});

output = createBuilder(subject);
yield output.build();

let result = output.read();
expect(result).to.have.property('foo.template.lint-test.js');

let contents = result['foo.template.lint-test.js'];
expect(contents.trim()).to.equal([
'describe(\'TemplateLint | foo\', function() {',
'',
' it(\'templates/application.hbs\', function() {',
' // test failed',
(
' var error = new chai.AssertionError(\'templates/application.hbs should pass TemplateLint.\\n\\n' +
'block-indentation: Incorrect indentation for `div` beginning at L2:C0. Expected `</div>` ending at L5:C9 to be at an indentation of 0 but was found at 3. (templates/application @ L5:C9): \\n' +
'`<div>\\n <p>\\n </p>\\n </div>`\\n' +
'block-indentation: Incorrect indentation for `p` beginning at L3:C2. Expected `</p>` ending at L4:C5 to be at an indentation of 2 but was found at 1. (templates/application @ L4:C5): \\n' +
'`<p>\\n </p>`\\n' +
'html-comments: HTML comment detected (templates/application): \\n' +
'`<!-- silly html comments -->`\');'
),
' error.stack = undefined;',
' throw error;',
' });',
'',
'});'
].join('\n'));
}));

it('generates empty test files if no "generateTestFile" option is provided', co.wrap(function *() {
input.copy(`${fixturePath}/with-errors`);

subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole
});

Expand All @@ -78,12 +198,9 @@ describe('broccoli-template-linter', function() {
it('prints warnings to console', co.wrap(function *() {
input.copy(`${fixturePath}/with-errors`);

subject = new TemplateLinter(`${input.path()}/app`, {
subject = TemplateLinter.create(`${input.path()}/app`, {
persist: false, // console messages are only printed when initially processed
console: mockConsole,
generateTestFile(moduleName, tests) {
return tests[0].errorMessage;
}
console: mockConsole
});

output = createBuilder(subject);
Expand All @@ -104,16 +221,15 @@ describe('broccoli-template-linter', function() {
isLocalizationFramework: true
};

subject = new TemplateLinter(`${input.path()}/app`, {
subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
project: {
addons: [
{ name: 'ember-cli-qunit' },
{ name: 'ember-cli-template-lint' },
localizationAddon
]
},
generateTestFile() { }
}
});

output = createBuilder(subject);
Expand All @@ -128,15 +244,14 @@ describe('broccoli-template-linter', function() {
it('does not print warning when bare-strings is not used when a localization addon is not present', co.wrap(function *() {
input.copy(`${fixturePath}/no-bare-strings`);

subject = new TemplateLinter(`${input.path()}/app`, {
subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
project: {
addons: [
{ name: 'ember-cli-qunit' },
{ name: 'ember-cli-template-lint' }
]
},
generateTestFile() { }
}
});

output = createBuilder(subject);
Expand All @@ -159,16 +274,15 @@ describe('broccoli-template-linter', function() {
isLocalizationFramework: true
};

subject = new TemplateLinter(`${input.path()}/app`, {
subject = TemplateLinter.create(`${input.path()}/app`, {
console: mockConsole,
project: {
addons: [
{ name: 'ember-cli-qunit' },
{ name: 'ember-cli-template-lint' },
localizationAddon
]
},
generateTestFile() { }
}
});

output = createBuilder(subject);
Expand Down
Loading