Skip to content
Draft
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
6 changes: 0 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions smoke-tests/node-template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
"name": "ember-test-node-template",
"version": "0.0.0",
"private": true,
"type": "module",
"description": "Node-focused smoke test template for ember-source",
"scripts": {
"test:node": "qunit tests/node/**/*-test.js"
},
"dependencies": {
"git-repo-info": "^2.1.1",
"html-differ": "^1.4.0",
"qunit": "^2.20.1",
"semver": "^7.6.0",
"simple-dom": "^1.4.0"
}
}
5 changes: 3 additions & 2 deletions smoke-tests/node-template/tests/node/app-boot-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const setupAppTest = require('./helpers/setup-app');
import setupAppTest from './helpers/setup-app.js';
import { register } from './helpers/assert-html-matches.js';

require('./helpers/assert-html-matches').register();
register();

QUnit.module('App Boot', function (hooks) {
setupAppTest(hooks);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const setupComponentTest = require('./helpers/setup-component');
import setupComponentTest from './helpers/setup-component.js';

QUnit.module('Components can be rendered without a DOM dependency', function (hooks) {
setupComponentTest(hooks);
Expand All @@ -22,7 +22,7 @@ QUnit.module('Components can be rendered without a DOM dependency', function (ho
function (assert) {
this.owner.register(
'component:fake-link',
class extends this.Ember.Component {
class extends this.Component {
tagName = 'link';
attributeBindings = ['href', 'rel'];
rel = 'canonical';
Expand Down
115 changes: 36 additions & 79 deletions smoke-tests/node-template/tests/node/fastboot-sandbox-test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
const fs = require('fs');
const vm = require('vm');
const SimpleDOM = require('simple-dom');
const { emberPath, loadEmber, clearEmber } = require('./helpers/load-ember');
import SimpleDOM from 'simple-dom';
import Application from 'ember-source/@ember/application/index.js';
import EmberRouter from 'ember-source/@ember/routing/router.js';
import { run } from 'ember-source/@ember/runloop/index.js';
import { precompile } from 'ember-source/ember-template-compiler/index.js';
import { createTemplateFactory } from 'ember-source/@ember/template-factory/index.js';

function compile(templateString, options) {
let templateSpec = precompile(templateString, options);
return createTemplateFactory(JSON.parse(templateSpec));
}

// This is based on what fastboot-server does
let HTMLSerializer = new SimpleDOM.HTMLSerializer(SimpleDOM.voidMap);

async function fastbootVisit(context, url) {
async function fastbootVisit(app, 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();
Expand All @@ -31,84 +36,36 @@ async function fastbootVisit(context, url) {
}
}

// 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: {}, require() {} },
};

// Set the global as `window`
sandbox.window = sandbox;
sandbox.window.self = sandbox;

let context = vm.createContext(sandbox);

let environmentSetupScript = new vm.Script(
`
var EmberENV = {
_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 = `
let Ember = module.exports;

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 = class extends Ember.Application {}.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);
});
let app;

hooks.afterEach(function () {
clearEmber();
if (app) {
run(app, 'destroy');
app = null;
}
});

QUnit.test('FastBoot: basic', async function (assert) {
let result = await fastbootVisit(this.context, '/');
let Router = class extends EmberRouter {};
Router.map(function () {
this.route('a');
this.route('b');
});

let registry = {
'router:main': Router,
'template:application': compile('<h1>Hello world!</h1>\n{{outlet}}'),
};

app = class extends Application {}.create({
autoboot: false,
Resolver: {
create: () => registry,
},
});

let result = await fastbootVisit(app, '/');

assert.equal(result.url, '/', 'landed on correct url');
assert.equal(
Expand Down
112 changes: 56 additions & 56 deletions smoke-tests/node-template/tests/node/fixtures/project.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,3 @@
module.exports = class Project {
static withDep(depOptions = {}, projectOptions = {}) {
let addons = projectOptions.addons || [];

return new Project({
...projectOptions,
addons: [...addons, new Addon(depOptions)],
});
}

static withTransientDep(transientDepOptions = {}, depOptions = {}, projectOptions = {}) {
let addons = depOptions.addons || [];

return Project.withDep(
{
...depOptions,
addons: [
...addons,
new Addon({
name: 'my-nested-addon',
version: '0.1.0',
...transientDepOptions,
}),
],
},
projectOptions
);
}

constructor({
name = 'my-app',
emberCliBabel,
dependencies = {},
devDependencies = {},
addons = [],
} = {}) {
this.name = () => name;
this.parent = null;
this.pkg = {
name,
dependencies: { ...dependencies },
devDependencies: { ...devDependencies },
};
this.addons = [...addons];

if (typeof emberCliBabel === 'string') {
this.pkg.devDependencies['ember-cli-babel'] = emberCliBabel;
}

reifyAddons(this);
addMissingAddons(this, this.pkg.devDependencies);
addMissingAddons(this, this.pkg.dependencies);
addMissingDeps(this, true);
}
};

class Addon {
constructor({
parent,
Expand Down Expand Up @@ -146,3 +90,59 @@ function addMissingDeps(parent, devDeps = false) {
}
}
}

export default class Project {
static withDep(depOptions = {}, projectOptions = {}) {
let addons = projectOptions.addons || [];

return new Project({
...projectOptions,
addons: [...addons, new Addon(depOptions)],
});
}

static withTransientDep(transientDepOptions = {}, depOptions = {}, projectOptions = {}) {
let addons = depOptions.addons || [];

return Project.withDep(
{
...depOptions,
addons: [
...addons,
new Addon({
name: 'my-nested-addon',
version: '0.1.0',
...transientDepOptions,
}),
],
},
projectOptions
);
}

constructor({
name = 'my-app',
emberCliBabel,
dependencies = {},
devDependencies = {},
addons = [],
} = {}) {
this.name = () => name;
this.parent = null;
this.pkg = {
name,
dependencies: { ...dependencies },
devDependencies: { ...devDependencies },
};
this.addons = [...addons];

if (typeof emberCliBabel === 'string') {
this.pkg.devDependencies['ember-cli-babel'] = emberCliBabel;
}

reifyAddons(this);
addMissingAddons(this, this.pkg.devDependencies);
addMissingAddons(this, this.pkg.dependencies);
addMissingDeps(this, true);
}
}
39 changes: 19 additions & 20 deletions smoke-tests/node-template/tests/node/helpers/assert-html-matches.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
const { HtmlDiffer } = require('html-differ');
import { HtmlDiffer } from 'html-differ';
import QUnit from 'qunit';

const htmlDiffer = new HtmlDiffer({
ignoreAttributes: ['id'],
ignoreWhitespaces: true,
});

module.exports = {
/*
* This assertion helper tests whether two fragments of Html 'appear'
* to match. In terms of fragments rendered by Ember, we want to explicitly
* ignore whitespace and certain attributes values, such as IDs, which Ember
* auto-generates. Attribute ordering is also ignored.
*/
register() {
QUnit.assert.htmlMatches = function (actual, expected, message) {
let isEqual = htmlDiffer.isEqual(actual, expected);
/*
* This assertion helper tests whether two fragments of Html 'appear'
* to match. In terms of fragments rendered by Ember, we want to explicitly
* ignore whitespace and certain attributes values, such as IDs, which Ember
* auto-generates. Attribute ordering is also ignored.
*/
export function register() {
QUnit.assert.htmlMatches = function (actual, expected, message) {
let isEqual = htmlDiffer.isEqual(actual, expected);

this.pushResult({
result: isEqual,
actual,
expected,
message,
});
};
},
};
this.pushResult({
result: isEqual,
actual,
expected,
message,
});
};
}
Loading
Loading