'use strict' const reactTemplates = require('../../src/reactTemplates') const testUtils = require('./utils/testUtils') const _ = require('lodash') const path = require('path') const RTCodeError = reactTemplates.RTCodeError const assert = require('assert') const cli = require('../../src/cli') const context = require('../../src/context') const omitStack = err => _.omit(err, 'stack', 'toIssue') /** * @param {ERR} err * @return {ERR} */ function normalizeError(err) { if (err) { err.msg = err.msg.replace(/\r/g, '') } return err } /** * @typedef {{index: number, line: number, column: number, msg: string, level: string, file: string}} ERR */ /** * @param {RTCodeError} err * @param {string} file * @return {ERR} */ function errorEqualMessage(err, file) { return { index: err.index, line: err.line, column: err.column, startOffset: err.startOffset, endOffset: err.endOffset, msg: err.message, level: 'ERROR', file } } describe('utils', () => { describe('#convertText', () => { const dataPath = path.resolve(__dirname, '..', 'data', 'invalid') const invalidFiles = [ // {file: 'sortByDragListItemRowExamples.rt', issue: new RTCodeError('x', 0, 160, 1, 1)} {file: 'if-with-scope/invalid-if-scope-1.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar(activeUsers.length)'", 0, 160, 1, 1)}, {file: 'if-with-scope/invalid-if-scope-2.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar[activeUsers || 0]'", 0, 158, 1, 1)}, { file: 'if-with-scope/invalid-if-scope-3.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.foo + activeUsers.length > this.bar'", 0, 172, 1, 1) }, {file: 'if-with-scope/invalid-if-scope-4.rt', issue: new RTCodeError("invalid scope mapping used in if part 'getCurrentActiveUsers().length'", 0, 170, 1, 1)}, {file: 'if-with-scope/invalid-if-scope-5.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar({activeUsers})'", 0, 155, 1, 1)}, {file: 'invalid-scope.rt', issue: new RTCodeError("invalid scope part 'a in a in a'", 0, 35, 1, 1)}, {file: 'invalid-html.rt', issue: new RTCodeError('Document should have a root element', -1, -1, -1, -1)}, {file: 'invalid-exp.rt', issue: new RTCodeError("Failed to parse text '\n {z\n'", 5, 13, 1, 6)}, { file: 'invalid-lambda.rt', issue: new RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1) }, { file: 'invalid-autobind.rt', issue: new RTCodeError("'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function.", 0, 132, 1, 1) }, // {file: 'invalid-js.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 0, 32, 1, 1)}, bug interduced due to scope parsing {file: 'invalid-single-root.rt', issue: new RTCodeError('Document should have no more than a single root element', 12, 23, 2, 1)}, {file: 'invalid-repeat-1.rt', issue: new RTCodeError("rt-repeat invalid 'in' expression 'a in b in c'", 9, 44, 2, 4)}, {file: 'invalid-repeat-2.rt', issue: new RTCodeError("root element may not have a 'rt-repeat' attribute", 0, 39, 1, 1)}, {file: 'invalid-rt-require-1.rt', issue: new RTCodeError("'rt-require' needs 'dependency' and 'as' attributes", 0, 14, 1, 1)}, {file: 'invalid-rt-require-2.rt', issue: new RTCodeError("'rt-require' may have no children", 0, 32, 1, 1)}, // {file: 'invalid-rt-require-duplicate.rt', issue: new RTCodeError("Line 1: Strict mode function may not have duplicate parameter names", 64, -1, -1, -1), options: {modules: 'amd'}}, {file: 'invalid-rt-import-1.rt', issue: new RTCodeError("'*' imports must have an 'as' attribute", 0, 36, 1, 1)}, {file: 'invalid-rt-import-2.rt', issue: new RTCodeError("default imports must have an 'as' attribute", 0, 42, 1, 1)}, {file: 'invalid-rt-import-3.rt', issue: new RTCodeError("'rt-import' needs 'name' and 'from' attributes", 0, 13, 1, 1)}, {file: 'invalid-rt-import-4.rt', issue: new RTCodeError("'rt-import' must be a toplevel node", 9, 54, 2, 4)}, {file: 'invalid-rt-template-1.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 88, 2, 4)}, {file: 'invalid-rt-template-2.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 90, 2, 4)}, {file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)}, {file: 'invalid-style-1.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)}, {file: 'invalid-style-2.rt', issue: new RTCodeError('style attribute keys cannot contain { } expressions', 35, 68, 2, 5)}, {file: 'invalid-virtual-1.rt', issue: new RTCodeError('Document should not have as root element', 0, 60, 1, 1)}, {file: 'invalid-virtual-2.rt', issue: new RTCodeError(" may not contain attributes other than 'rt-scope', 'rt-if' and 'rt-repeat'", 9, 119, 2, 4)} ] it('should convert text successfully', () => { invalidFiles.forEach(testFile => { const filename = path.join(dataPath, testFile.file) const html = testUtils.readFileNormalized(filename) let error = null try { reactTemplates.convertTemplateToReact(html) } catch (e) { error = e } assert.deepEqual(omitStack(error), omitStack(testFile.issue), 'Expect convertTemplateToReact to throw an error') }) invalidFiles.forEach(check) function check(testFile) { context.clear() const filename = path.join(dataPath, testFile.file) const options = {format: 'json', force: true} cli.handleSingleFile(_.assign(options, testFile.options), filename) assert.deepEqual(normalizeError(context.getMessages()[0]), errorEqualMessage(testFile.issue, filename), `Expect cli to produce valid output messages ${testFile.file}`) } }) }) })