Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix rendering engine usage within a fastboot sandbox #19124

Merged
merged 2 commits into from
Sep 3, 2020
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
14 changes: 0 additions & 14 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' ],

Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@
},
"devDependencies": {
"@babel/preset-env": "^7.9.5",
"@glimmer/compiler": "^0.59.0",
"@glimmer/compiler": "^0.59.1",
"@glimmer/env": "^0.1.7",
"@glimmer/global-context": "^0.59.0",
"@glimmer/interfaces": "^0.59.0",
"@glimmer/node": "^0.59.0",
"@glimmer/opcode-compiler": "^0.59.0",
"@glimmer/program": "^0.59.0",
"@glimmer/reference": "^0.59.0",
"@glimmer/runtime": "^0.59.0",
"@glimmer/validator": "^0.59.0",
"@glimmer/global-context": "^0.59.1",
"@glimmer/interfaces": "^0.59.1",
"@glimmer/node": "^0.59.1",
"@glimmer/opcode-compiler": "^0.59.1",
"@glimmer/program": "^0.59.1",
"@glimmer/reference": "^0.59.1",
"@glimmer/runtime": "^0.59.1",
"@glimmer/validator": "^0.59.1",
"@simple-dom/document": "^1.4.0",
"@types/qunit": "^2.9.1",
"@types/rsvp": "^4.0.3",
Expand Down
8 changes: 8 additions & 0 deletions tests/node/component-rendering-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<a href={{this.someHref}}>Some Link</a>');

assert.ok(html.match(/<a href="https:\/\/foo.com\/">Some Link<\/a>/));
});
});
120 changes: 120 additions & 0 deletions tests/node/fastboot-sandbox-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const fs = require('fs');
const vm = require('vm');
const SimpleDOM = require('simple-dom');
const { emberPath, loadEmber, clearEmber } = require('./helpers/load-ember');

// 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();
}
}

// 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('<h1>Hello world!</h1>\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,
'<body><h1>Hello world!</h1>\n<!----></body>',
'results in expected HTML'
);
});
});
14 changes: 11 additions & 3 deletions tests/node/helpers/load-ember.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Loading