'use strict'; var test = require('tape'); var reactTemplates = require('../../src/reactTemplates'); var context = require('../../src/context'); var util = require('./util'); var readFileNormalized = util.readFileNormalized; var compareAndWrite = util.compareAndWrite; var fs = require('fs'); var _ = require('lodash'); var path = require('path'); var RTCodeError = reactTemplates.RTCodeError; var dataPath = path.resolve(__dirname, '..', 'data'); var invalidFiles = [ {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 use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1)}, {file: 'invalid-js.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 0, 32, 1, 1)}, {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.rt', issue: new RTCodeError('rt-repeat invalid \'in\' expression \'a in b in c\'', 0, 35, 1, 1)}, {file: 'invalid-rt-require.rt', issue: new RTCodeError("rt-require needs 'dependency' and 'as' attributes", 0, 14, 1, 1)}, {file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)}, {file: 'invalid-style.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)} ]; test('invalid tests', function (t) { t.plan(invalidFiles.length); invalidFiles.forEach(check); function check(testFile) { var filename = path.join(dataPath, testFile.file); var html = util.readFileNormalized(filename); var error = null; try { reactTemplates.convertTemplateToReact(html); } catch (e) { error = e; } t.deepEqual(omitStack(error), omitStack(testFile.issue), 'Expect convertTemplateToReact to throw an error'); } }); function omitStack(err) { return _.omit(err, 'stack', 'toIssue'); } /** * @param {ERR} err * @return {ERR} */ function normalizeError(err) { err.msg = err.msg.replace(/\r/g, ''); return err; } test('invalid tests json', function (t) { var cli = require('../../src/cli'); t.plan(invalidFiles.length); invalidFiles.forEach(check); function check(testFile) { context.clear(); var filename = path.join(dataPath, testFile.file); var options = {format: 'json', force: true}; cli.handleSingleFile(options, filename); t.deepEqual(normalizeError(context.getMessages()[0]), errorEqualMessage(testFile.issue, filename), 'Expect cli to produce valid output messages'); } }); /** * @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: file }; } test('rt-if with rt-scope test', function (t) { var files = ['if-with-scope/valid-if-scope.rt']; testFiles(t, files); }); test('conversion test', function (t) { var files = ['div.rt', 'test.rt', 'repeat.rt', 'inputs.rt', 'require.rt']; testFiles(t, files); }); test('prop template conversion test', function (t) { var options = { propTemplates: { List: { Row: {prop: 'renderRow', arguments: ['rowData']} } } }; var files = ['propTemplates/simpleTemplate.rt', 'propTemplates/templateInScope.rt', 'propTemplates/implicitTemplate.rt', 'propTemplates/twoTemplates.rt']; testFiles(t, files, options); }); function checkFile(t, options, testFile) { var filename = path.join(dataPath, testFile); var html = readFileNormalized(filename); var expected = readFileNormalized(filename + '.js'); var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim(); compareAndWrite(t, actual, expected, filename); } function testFiles(t, files, options) { t.plan(files.length); files.forEach(checkFile.bind(this, t, options)); } test('conversion test - native', function (t) { var options = { propTemplates: { MyComp: { Row: {prop: 'renderRow', arguments: ['rowData']} } }, native: true }; var files = ['nativeView.rt', 'listViewTemplate.rt', 'listViewAndCustomTemplate.rt']; testFiles(t, files, options); }); test('convert div with all module types', function (t) { var files = [ {source: 'div.rt', expected: 'div.rt.commonjs.js', options: {modules: 'commonjs'}}, {source: 'div.rt', expected: 'div.rt.amd.js', options: {modules: 'amd', name: 'div'}}, {source: 'div.rt', expected: 'div.rt.globals.js', options: {modules: 'none', name: 'div'}}, {source: 'div.rt', expected: 'div.rt.es6.js', options: {modules: 'es6', name: 'div'}}, {source: 'div.rt', expected: 'div.rt.typescript.ts', options: {modules: 'typescript'}} ]; t.plan(files.length); files.forEach(check); function check(testData) { var filename = path.join(dataPath, testData.source); var html = readFileNormalized(filename); var expected = readFileNormalized(path.join(dataPath, testData.expected)); // var expected = fs.readFileSync(filename.replace(".html", ".js")).toString(); var actual = reactTemplates.convertTemplateToReact(html, testData.options).replace(/\r/g, '').trim(); compareAndWrite(t, actual, expected, filename); } }); test('convert jsrt and test source results', function (t) { var files = ['simple.jsrt']; t.plan(files.length); files.forEach(check); function check(file) { var filename = path.join(dataPath, file); var js = readFileNormalized(filename); var expected = readFileNormalized(path.join(dataPath, file.replace('.jsrt', '.js'))); // var expected = fs.readFileSync(filename.replace(".html", ".js")).toString(); var actual = reactTemplates.convertJSRTToJS(js, context).replace(/\r/g, '').trim(); compareAndWrite(t, actual, expected, filename); } }); test('html tests', function (t) { var files = ['scope.rt', 'scope-trailing-semicolon.rt', 'scope-variable-references.rt', 'lambda.rt', 'eval.rt', 'props.rt', 'custom-element.rt', 'style.rt', 'concat.rt', 'js-in-attr.rt', 'props-class.rt', 'rt-class.rt', 'className.rt', 'svg.rt', 'repeat-literal-collection.rt', 'scope-evaluated-after-repeat.rt', 'scope-evaluated-after-repeat2.rt', 'scope-evaluated-after-if.rt', 'scope-obj.rt' ]; t.plan(files.length); files.forEach(check); function check(testFile) { var filename = path.join(dataPath, testFile); var code = ''; try { var html = fs.readFileSync(filename).toString(); var expected = readFileNormalized(filename + '.html'); // var expected = fs.readFileSync(filename.replace(".html", ".js")).toString(); code = reactTemplates.convertTemplateToReact(html).replace(/\r/g, ''); var actual = util.codeToHtml(code); actual = util.normalizeHtml(actual); expected = util.normalizeHtml(expected); var equal = compareAndWrite(t, actual, expected, filename); if (!equal) { fs.writeFileSync(filename + '.code.js', code); } } catch (e) { console.log(testFile, e); fs.writeFileSync(filename + '.code.js', code); } } }); test('test context', function (t) { context.clear(); t.equal(context.hasErrors(), false); context.error('hi', '', 1, 1); t.equal(context.hasErrors(), true); context.clear(); t.equal(context.hasErrors(), false); t.end(); }); test('test shell', function (t) { var shell = require('../../src/shell'); var newContext = _.cloneDeep(context); var outputJSON = ''; newContext.options.format = 'json'; newContext.report = function (text) { outputJSON = text; }; var r = shell.printResults(newContext); t.equal(r, 0); context.error('hi', '', 1, 1); r = shell.printResults(newContext); t.equal(r, 1); var output = JSON.parse(outputJSON); t.deepEqual(output, [{column: 1, endOffset: -1, file: null, index: -1, level: 'ERROR', line: 1, msg: 'hi', startOffset: -1}]); context.clear(); t.end(); }); test('test shell', function (t) { var filename = path.join(dataPath, 'div.rt'); var cli = require('../../src/cli'); var r = cli.execute(`${filename} -r --dry-run`); t.equal(r, 0); t.end(); }); test('test convertText', function (t) { var texts = [ {input: '{}', expected: '()'}, {input: "a {'b'}", expected: '"a "+(\'b\')'} ]; t.plan(texts.length); texts.forEach(check); function check(testData) { var r = reactTemplates._test.convertText({}, {}, testData.input); t.equal(r, testData.expected); } }); test('util.isStale', function (t) { var a = path.join(dataPath, 'a.tmp'); var b = path.join(dataPath, 'b.tmp'); fs.writeFileSync(a, 'actual'); fs.writeFileSync(b, 'actual'); var mtime1 = new Date(1995, 11, 17, 3, 24, 0); fs.utimesSync(a, mtime1, mtime1); var mtime2 = new Date(1995, 11, 17, 3, 24, 1); fs.utimesSync(b, mtime2, mtime2); var fsUtil = require('../../src/fsUtil'); var actual = fsUtil.isStale(a, b); t.equal(actual, false); actual = fsUtil.isStale(b, a); t.equal(actual, true); fs.unlinkSync(a); fs.unlinkSync(b); t.end(); });