diff --git a/.eslintrc.js b/.eslintrc.js index a850e9954af..9512c474dba 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -166,20 +166,6 @@ module.exports = { 'disable-features/disable-generator-functions': 'off', }), }, - { - // matches node-land files that aren't shipped to consumers (allows using Node 6+ features) - files: [ - 'broccoli/**/*.js', - 'tests/node/**/*.js', - 'ember-cli-build.js', - 'rollup.config.js', - 'd8-runner.js', - ], - - rules: { - 'node/no-unsupported-features': ['error', { version: 6 }], - } - }, { files: [ 'node-tests/**/*.js' ], diff --git a/tests/node/component-rendering-test.js b/tests/node/component-rendering-test.js index e8fcce14a91..a5e9c17aeef 100644 --- a/tests/node/component-rendering-test.js +++ b/tests/node/component-rendering-test.js @@ -33,4 +33,12 @@ QUnit.module('Components can be rendered without a DOM dependency', function(hoo assert.ok(html.match(/rel="canonical"/)); }); + + QUnit.test('attributes requiring protocol sanitization do not error', function(assert) { + this.set('someHref', 'https://foo.com/'); + + let html = this.render('Some Link'); + + assert.ok(html.match(/Some Link<\/a>/)); + }); }); diff --git a/tests/node/fastboot-sandbox-test.js b/tests/node/fastboot-sandbox-test.js new file mode 100644 index 00000000000..87a69bfa9c0 --- /dev/null +++ b/tests/node/fastboot-sandbox-test.js @@ -0,0 +1,137 @@ +const fs = require('fs'); +const vm = require('vm'); +const SimpleDOM = require('simple-dom'); +const { emberPath, loadEmber, clearEmber } = require('./helpers/load-ember'); + +function assertHTMLMatches(assert, actualHTML, expectedHTML) { + assert.ok(actualHTML.match(expectedHTML), actualHTML + ' matches ' + expectedHTML); +} + +function handleError(assert) { + return function(error) { + assert.ok(false, error.stack); + }; +} + +// This is based on what fastboot-server does +let HTMLSerializer = new SimpleDOM.HTMLSerializer(SimpleDOM.voidMap); + +async function fastbootVisit(context, url) { + let doc = new SimpleDOM.Document(); + let rootElement = doc.body; + let options = { isBrowser: false, document: doc, rootElement: rootElement }; + + let { app } = context; + + await app.boot(); + + let instance = await app.buildInstance(); + + try { + await instance.boot(options); + await instance.visit(url, options); + + return { + url: instance.getURL(), + title: doc.title, + body: HTMLSerializer.serialize(rootElement), + }; + } finally { + instance.destroy(); + } +} + +function assertFastbootResult(assert, expected) { + return function(actual) { + assert.equal(actual.url, expected.url); + assertHTMLMatches(assert, actual.body, expected.body); + }; +} + +// essentially doing the same as what is done in FastBoot 3.1.0 +// https://github.com/ember-fastboot/fastboot/blob/v3.1.0/src/sandbox.js +function buildSandboxContext(precompile) { + let URL = require('url'); + + let sandbox = { + console, + setTimeout, + clearTimeout, + URL, + + // Convince jQuery not to assume it's in a browser + module: { exports: {} }, + }; + + // Set the global as `window` + sandbox.window = sandbox; + sandbox.window.self = sandbox; + + let context = vm.createContext(sandbox); + + let environmentSetupScript = new vm.Script( + ` +var EmberENV = { + _TEMPLATE_ONLY_GLIMMER_COMPONENTS: true, + _APPLICATION_TEMPLATE_WRAPPER: false, + _DEFAULT_ASYNC_OBSERVERS: true, + _JQUERY_INTEGRATION: false, +};`, + { filename: 'prepend.js' } + ); + environmentSetupScript.runInContext(context); + + let emberSource = fs.readFileSync(emberPath, { encoding: 'utf-8' }); + let emberScript = new vm.Script(emberSource, { filename: emberPath }); + emberScript.runInContext(context); + + let applicationSource = ` +class Router extends Ember.Router {} +Router.map(function() { + this.route('a'); + this.route('b'); +}); + +const registry = { + 'router:main': Router, + 'template:application': ${precompile('

Hello world!

\n{{outlet}}')} +}; + +class Resolver extends Ember.Object { + resolve(specifier) { + return registry[specifier]; + } +} + +var app = Ember.Application.extend().create({ + autoboot: false, + Resolver, +}); +`; + let appScript = new vm.Script(applicationSource, { filename: 'app.js' }); + appScript.runInContext(context); + + return context; +} + +QUnit.module('Ember.Application - visit() Integration Tests', function(hooks) { + hooks.beforeEach(function() { + let { precompile } = loadEmber(); + this.context = buildSandboxContext(precompile); + }); + + hooks.afterEach(function() { + clearEmber(); + }); + + QUnit.test('FastBoot: basic', async function(assert) { + let result = await fastbootVisit(this.context, '/'); + + assert.equal(result.url, '/', 'landed on correct url'); + assert.equal( + result.body, + '

Hello world!

\n', + 'results in expected HTML' + ); + }); +}); diff --git a/tests/node/helpers/load-ember.js b/tests/node/helpers/load-ember.js index 7a9da12fbd7..4b6d5df56a3 100644 --- a/tests/node/helpers/load-ember.js +++ b/tests/node/helpers/load-ember.js @@ -7,19 +7,27 @@ const templateCompilerPath = path.join(distPath, 'ember-template-compiler'); // properly to avoid the @glimmer/validator assertion const originalGlobalSymbols = Object.getOwnPropertySymbols(global).map(sym => [sym, global[sym]]); +module.exports.emberPath = require.resolve(emberPath); + module.exports.loadEmber = function() { let Ember = require(emberPath); - let precompile = require(templateCompilerPath).precompile; + let _precompile = require(templateCompilerPath).precompile; + + let precompile = function(templateString, options) { + let templateSpec = _precompile(templateString, options); + + return `Ember.HTMLBars.template(${templateSpec})`; + }; let compile = function(templateString, options) { - let templateSpec = precompile(templateString, options); + let templateSpec = _precompile(templateString, options); let template = new Function('return ' + templateSpec)(); return Ember.HTMLBars.template(template); }; - return { Ember, compile }; + return { Ember, compile, precompile }; }; module.exports.clearEmber = function() {