DEV: Migrate to Ember CLI (#11932)

This encompasses a lot of work done over the last year, much of which
has already been merged into master. This is the final set of changes
required to get Ember CLI running locally for development.

From here on it will be bug fixes / enhancements.

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Co-authored-by: romanrizzi <rizziromanalejandro@gmail.com>

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Co-authored-by: romanrizzi <rizziromanalejandro@gmail.com>
This commit is contained in:
Robin Ward 2021-02-03 14:22:20 -05:00 committed by GitHub
parent 8ad5284cf7
commit 61f5d501cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 23103 additions and 1247 deletions

View File

@ -14,6 +14,7 @@ import {
import { getOwner, setDefaultOwner } from "discourse-common/lib/get-owner";
import { setApplication, setResolver } from "@ember/test-helpers";
import { setupS3CDN, setupURL } from "discourse-common/lib/get-url";
import Application from "../app";
import MessageBus from "message-bus-client";
import PreloadStore from "discourse/lib/preload-store";
import QUnit from "qunit";
@ -26,6 +27,8 @@ import { clearAppEventsCache } from "discourse/services/app-events";
import { createHelperContext } from "discourse-common/lib/helpers";
import deprecated from "discourse-common/lib/deprecated";
import { flushMap } from "discourse/models/store";
import { registerObjects } from "discourse/pre-initializers/inject-discourse-objects";
import { setupApplicationTest } from "ember-qunit";
import sinon from "sinon";
const Plugin = $.fn.modal;
@ -55,9 +58,33 @@ function AcceptanceModal(option, _relatedTarget) {
});
}
export default function setupTests(app, container) {
let app;
let started = false;
function createApplication(config, settings) {
app = Application.create(config);
setApplication(app);
setResolver(buildResolver("discourse").create({ namespace: app }));
let container = app.__registry__.container();
app.__container__ = container;
setDefaultOwner(container);
if (!started) {
app.start();
started = true;
}
app.SiteSettings = settings;
registerObjects(container, app);
return app;
}
function setupTestsCommon(application, container, config) {
application.rootElement = "#ember-testing";
application.setupForTesting();
application.injectTestHelpers();
sinon.config = {
injectIntoThis: false,
injectInto: null,
@ -69,13 +96,6 @@ export default function setupTests(app, container) {
// Stop the message bus so we don't get ajax calls
MessageBus.stop();
app.rootElement = "#ember-testing";
app.setupForTesting();
app.SiteSettings = currentSettings();
app.start();
bootbox.$body = $("#ember-testing");
$.fn.modal = AcceptanceModal;
// disable logster error reporting
if (window.Logster) {
window.Logster.enabled = false;
@ -83,6 +103,8 @@ export default function setupTests(app, container) {
window.Logster = { enabled: false };
}
$.fn.modal = AcceptanceModal;
let server;
Object.defineProperty(window, "server", {
@ -123,7 +145,14 @@ export default function setupTests(app, container) {
});
QUnit.testStart(function (ctx) {
bootbox.$body = $("#ember-testing");
let settings = resetSettings();
if (config) {
// Ember CLI testing environment
app = createApplication(config, settings);
}
server = createPretender;
server.handlers = [];
applyDefaultHandlers(server);
@ -190,10 +219,12 @@ export default function setupTests(app, container) {
$(".modal-backdrop").remove();
flushMap();
// ensures any event not removed is not leaking between tests
// most likely in intialisers, other places (controller, component...)
// should be fixed in code
clearAppEventsCache(getOwner(this));
if (!setupApplicationTest) {
// ensures any event not removed is not leaking between tests
// most likely in intialisers, other places (controller, component...)
// should be fixed in code
clearAppEventsCache(getOwner(this));
}
MessageBus.unsubscribe("*");
server = null;
@ -226,7 +257,23 @@ export default function setupTests(app, container) {
// forces 0 as duration for all jquery animations
jQuery.fx.off = true;
setApplication(app);
setDefaultOwner(container);
setApplication(application);
setDefaultOwner(application.__container__);
resetSite();
}
export function setupTestsLegacy(application) {
app = application;
setResolver(buildResolver("discourse").create({ namespace: app }));
setupTestsCommon(application, app.__container__);
app.SiteSettings = currentSettings();
app.start();
}
export default function setupTests(config) {
let settings = resetSettings();
app = createApplication(config, settings);
setupTestsCommon(app, app.__container__, config);
}

View File

@ -0,0 +1,13 @@
import config from "../config/environment";
import { setEnvironment } from "discourse-common/config/environment";
import { start } from "ember-qunit";
setEnvironment("testing");
document.addEventListener("discourse-booted", () => {
let setupTests = require("discourse/tests/setup-tests").default;
Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION = false;
setupTests(config.APP);
start();
});

View File

@ -50,7 +50,5 @@ document.write(
"<style>#ember-testing-container { position: absolute; background: white; bottom: 0; right: 0; width: 640px; height: 384px; overflow: auto; z-index: 9999; border: 1px solid #ccc; } #ember-testing { zoom: 50%; }</style>"
);
let app = window.Discourse;
app.injectTestHelpers();
let setupTests = require("discourse/tests/setup-tests").default;
setupTests(app, app.__container__);
let setupTestsLegacy = require("discourse/tests/setup-tests").setupTestsLegacy;
setupTestsLegacy(window.Discourse);

View File

@ -9,10 +9,6 @@ discourseModule("Unit | Controller | avatar-selector", function (hooks) {
});
test("avatarTemplate", function (assert) {
const avatarSelectorController = this.owner.lookup(
"controller:avatar-selector"
);
const user = EmberObject.create({
avatar_template: "avatar",
system_avatar_template: "system",
@ -22,8 +18,9 @@ discourseModule("Unit | Controller | avatar-selector", function (hooks) {
gravatar_avatar_upload_id: 2,
custom_avatar_upload_id: 3,
});
avatarSelectorController.setProperties({ user });
const avatarSelectorController = this.getController("avatar-selector", {
user,
});
user.set("avatar_template", "system");
assert.equal(

View File

@ -4,8 +4,8 @@ import { test } from "qunit";
discourseModule("Unit | Controller | create-account", function () {
test("basicUsernameValidation", async function (assert) {
const testInvalidUsername = async function (username, expectedReason) {
const controller = await this.owner.lookup("controller:create-account");
const testInvalidUsername = async (username, expectedReason) => {
const controller = this.getController("create-account");
controller.set("accountUsername", username);
let validation = controller.basicUsernameValidation(username);
@ -15,7 +15,7 @@ discourseModule("Unit | Controller | create-account", function () {
expectedReason,
"username validation reason: " + username + ", " + expectedReason
);
}.bind(this);
};
testInvalidUsername("", undefined);
testInvalidUsername("x", I18n.t("user.username.too_short"));
@ -40,7 +40,7 @@ discourseModule("Unit | Controller | create-account", function () {
});
test("passwordValidation", async function (assert) {
const controller = await this.owner.lookup("controller:create-account");
const controller = this.getController("create-account");
controller.set("authProvider", "");
controller.set("accountEmail", "pork@chops.com");

View File

@ -3,7 +3,7 @@ import { test } from "qunit";
discourseModule("Unit | Controller | history", function () {
test("displayEdit", async function (assert) {
const HistoryController = this.owner.lookup("controller:history");
const HistoryController = this.getController("history");
HistoryController.setProperties({
model: { last_revision: 3, current_revision: 3, can_edit: false },

View File

@ -1,25 +1,23 @@
import EmberObject from "@ember/object";
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
import { test } from "qunit";
discourseModule("Unit | Controller | preferences/account", function () {
test("updating of associated accounts", function (assert) {
const controller = this.owner.lookup("controller:preferences/account");
controller.setProperties({
const controller = this.getController("preferences/account", {
siteSettings: {
enable_google_oauth2_logins: true,
},
model: EmberObject.create({
model: {
id: 70,
second_factor_enabled: true,
is_anonymous: true,
}),
currentUser: EmberObject.create({
},
currentUser: {
id: 1234,
}),
site: EmberObject.create({
},
site: {
isMobileDevice: false,
}),
},
});
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);

View File

@ -3,15 +3,11 @@ import { test } from "qunit";
discourseModule("Unit | Controller | preferences/second-factor", function () {
test("displayOAuthWarning when OAuth login methods are enabled", function (assert) {
const controller = this.owner.lookup(
"controller:preferences/second-factor"
);
controller.setProperties({
const controller = this.getController("preferences/second-factor", {
siteSettings: {
enable_google_oauth2_logins: true,
},
});
assert.equal(controller.get("displayOAuthWarning"), true);
});
});

View File

@ -1,4 +1,3 @@
import EmberObject from "@ember/object";
import createStore from "discourse/tests/helpers/create-store";
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
import { test } from "qunit";
@ -12,11 +11,10 @@ discourseModule("Unit | Controller | reorder-categories", function () {
categories.push(store.createRecord("category", { id: i, position: 0 }));
}
const site = EmberObject.create({ categories: categories });
const reorderCategoriesController = this.owner.lookup(
"controller:reorder-categories"
const reorderCategoriesController = this.getController(
"reorder-categories",
{ site: { categories } }
);
reorderCategoriesController.setProperties({ site });
reorderCategoriesController.reorder();
reorderCategoriesController
@ -52,14 +50,11 @@ discourseModule("Unit | Controller | reorder-categories", function () {
slug: "other",
});
const categories = [child2, parent, other, child1];
const expectedOrderSlugs = ["parent", "child2", "child1", "other"];
const site = EmberObject.create({ categories: categories });
const reorderCategoriesController = this.owner.lookup(
"controller:reorder-categories"
const reorderCategoriesController = this.getController(
"reorder-categories",
{ site: { categories: [child2, parent, other, child1] } }
);
reorderCategoriesController.setProperties({ site });
reorderCategoriesController.reorder();
assert.deepEqual(
@ -89,12 +84,10 @@ discourseModule("Unit | Controller | reorder-categories", function () {
slug: "test",
});
const categories = [elem1, elem2, elem3];
const site = EmberObject.create({ categories: categories });
const reorderCategoriesController = this.owner.lookup(
"controller:reorder-categories"
const reorderCategoriesController = this.getController(
"reorder-categories",
{ site: { categories: [elem1, elem2, elem3] } }
);
reorderCategoriesController.setProperties({ site });
reorderCategoriesController.actions.change.call(
reorderCategoriesController,
@ -136,12 +129,10 @@ discourseModule("Unit | Controller | reorder-categories", function () {
slug: "test",
});
const categories = [elem1, child1, elem2, elem3];
const site = EmberObject.create({ categories: categories });
const reorderCategoriesController = this.owner.lookup(
"controller:reorder-categories"
const reorderCategoriesController = this.getController(
"reorder-categories",
{ site: { categories: [elem1, child1, elem2, elem3] } }
);
reorderCategoriesController.setProperties({ site });
reorderCategoriesController.actions.change.call(
reorderCategoriesController,
@ -190,12 +181,10 @@ discourseModule("Unit | Controller | reorder-categories", function () {
slug: "test",
});
const categories = [elem1, child1, child2, elem2, elem3];
const site = EmberObject.create({ categories: categories });
const reorderCategoriesController = this.owner.lookup(
"controller:reorder-categories"
const reorderCategoriesController = this.getController(
"reorder-categories",
{ site: { categories: [elem1, child1, child2, elem2, elem3] } }
);
reorderCategoriesController.setProperties({ site });
reorderCategoriesController.reorder();
reorderCategoriesController.actions.moveDown.call(

View File

@ -1,19 +0,0 @@
import { module, test } from "qunit";
/* global BreakString:true */
module("Unit | Utility | breakString", function () {
test("breakString", function (assert) {
const b = (s, hint) => new BreakString(s).break(hint);
assert.equal(b("hello"), "hello");
assert.equal(b("helloworld"), "helloworld");
assert.equal(b("HeMans11"), "He<wbr>&#8203;Mans<wbr>&#8203;11");
assert.equal(b("he_man"), "he_<wbr>&#8203;man");
assert.equal(b("he11111"), "he<wbr>&#8203;11111");
assert.equal(b("HRCBob"), "HRC<wbr>&#8203;Bob");
assert.equal(
b("bobmarleytoo", "Bob Marley Too"),
"bob<wbr>&#8203;marley<wbr>&#8203;too"
);
});
});

View File

@ -2,13 +2,21 @@ import {
currentUser,
discourseModule,
} from "discourse/tests/helpers/qunit-helpers";
import DocumentTitle from "discourse/services/document-title";
import AppEvents from "discourse/services/app-events";
import Session from "discourse/models/session";
import { test } from "qunit";
discourseModule("Unit | Service | document-title", function (hooks) {
hooks.beforeEach(function () {
this.documentTitle = this.container.lookup("service:document-title");
const session = Session.current();
session.hasFocus = true;
this.documentTitle = DocumentTitle.create({
session,
appEvents: AppEvents.create(),
});
this.documentTitle.currentUser = null;
this.container.lookup("session:main").hasFocus = true;
});
hooks.afterEach(function () {

View File

@ -1,12 +1,12 @@
import Component from "@ember/component";
import { afterRender } from "discourse-common/utils/decorators";
import componentTest from "discourse/tests/helpers/component-test";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import { moduleForComponent } from "ember-qunit";
import componentTest, {
setupRenderingTest,
} from "discourse/tests/helpers/component-test";
import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers";
import hbs from "htmlbars-inline-precompile";
const fooComponent = Component.extend({
layoutName: "foo-component",
classNames: ["foo-component"],
baz: null,
@ -29,23 +29,25 @@ const fooComponent = Component.extend({
},
});
moduleForComponent("utils:decorators", { integration: true });
discourseModule("utils:decorators", function (hooks) {
setupRenderingTest(hooks);
componentTest("afterRender", {
template: "{{foo-component baz=baz}}",
componentTest("afterRender", {
template: hbs`{{foo-component baz=baz}}`,
beforeEach() {
this.registry.register("component:foo-component", fooComponent);
this.set("baz", 0);
},
beforeEach() {
this.registry.register("component:foo-component", fooComponent);
this.set("baz", 0);
},
test(assert) {
assert.ok(exists(document.querySelector(".foo-component")));
assert.equal(this.baz, 1);
async test(assert) {
assert.ok(exists(document.querySelector(".foo-component")));
assert.equal(this.baz, 1);
this.clearRender();
await this.clearRender();
assert.ok(!exists(document.querySelector(".foo-component")));
assert.equal(this.baz, 1);
},
assert.ok(!exists(document.querySelector(".foo-component")));
assert.equal(this.baz, 1);
},
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
{
"private": true,
"workspaces": [
"discourse",
"admin",
"discourse-common",
"discourse-hbr",
"discourse-widget-hbs",
"pretty-text",
"select-kit"
]
}

View File

@ -15,9 +15,10 @@
"start": "ember serve"
},
"dependencies": {
"ember-auto-import": "^1.5.3",
"ember-cli-babel": "^7.13.0",
"ember-cli-htmlbars": "^4.2.0",
"ember-auto-import": "^1.5.3"
"xss": "^1.0.8"
},
"devDependencies": {
"@ember/optional-features": "^1.1.0",

View File

@ -52,7 +52,9 @@ define("@ember/test-helpers", () => {
return window[attr](...arguments);
};
});
helpers.triggerKeyEvent = window.keyEvent;
helpers.triggerKeyEvent = function () {
return window.keyEvent(...arguments);
};
return helpers;
});
define("pretender", () => {

File diff suppressed because it is too large Load Diff

View File

@ -279,9 +279,13 @@ class Compiler {
}
}
function compile(template) {
const preprocessor = Ember.__loader.require("@glimmer/syntax");
const compiled = preprocessor.preprocess(template);
const loader = typeof Ember !== "undefined" ? Ember.__loader.require : require;
function compile(template, glimmer) {
if (!glimmer) {
glimmer = loader("@glimmer/syntax");
}
const compiled = glimmer.preprocess(template);
const compiler = new Compiler(compiled);
let code = compiler.compile();
@ -305,7 +309,7 @@ function error(path, state, msg) {
);
}
exports.WidgetHbsCompiler = function (babel) {
const WidgetHbsCompiler = function (babel) {
let t = babel.types;
return {
visitor: {
@ -353,11 +357,16 @@ exports.WidgetHbsCompiler = function (babel) {
.join("");
try {
path.replaceWithSourceString(compile(template));
path.replaceWithSourceString(
compile(template, WidgetHbsCompiler.glimmer)
);
} catch (e) {
console.error("widget hbs error", e.toString());
return error(path, state, e.toString());
}
},
},
};
};
exports.WidgetHbsCompiler = WidgetHbsCompiler;

View File

@ -66,7 +66,7 @@ acceptance("Poll breakdown", function (needs) {
"shows the breakdown button when poll_groupable_user_fields is non-empty"
);
await click(".poll-show-breakdown:first");
await click(".poll-show-breakdown");
assert.equal(
queryAll(".poll-breakdown-total-votes")[0].textContent.trim(),
@ -88,10 +88,10 @@ acceptance("Poll breakdown", function (needs) {
test("Changing the display mode from percentage to count", async function (assert) {
await visit("/t/-/topic_with_pie_chart_poll");
await click(".poll-show-breakdown:first");
await click(".poll-show-breakdown");
assert.equal(
queryAll(".poll-breakdown-option-count:first")[0].textContent.trim(),
queryAll(".poll-breakdown-option-count")[0].textContent.trim(),
"40.0%",
"displays the correct vote percentage"
);
@ -99,7 +99,7 @@ acceptance("Poll breakdown", function (needs) {
await click(".modal-tabs .count");
assert.equal(
queryAll(".poll-breakdown-option-count:first")[0].textContent.trim(),
queryAll(".poll-breakdown-option-count")[0].textContent.trim(),
"2",
"displays the correct vote count"
);

View File

@ -68,15 +68,15 @@ acceptance("Rendering polls with bar charts - desktop", function (needs) {
await click("button.toggle-results");
assert.equal(
queryAll(".poll-voters:first li").length,
queryAll(".poll-voters:nth-of-type(1) li").length,
25,
"it should display the right number of voters"
);
await click(".poll-voters-toggle-expand:first a");
await click(".poll-voters-toggle-expand:nth-of-type(1) a");
assert.equal(
queryAll(".poll-voters:first li").length,
queryAll(".poll-voters:nth-of-type(1) li").length,
26,
"it should display the right number of voters"
);
@ -91,20 +91,20 @@ acceptance("Rendering polls with bar charts - desktop", function (needs) {
await click("button.toggle-results");
assert.equal(
queryAll(".poll-voters:first li").length,
queryAll(".poll-voters:nth-of-type(1) li").length,
25,
"it should display the right number of voters"
);
assert.notOk(
queryAll(".poll-voters:first li:first a").attr("href"),
queryAll(".poll-voters:nth-of-type(1) li:nth-of-type(1) a").attr("href"),
"user URL does not exist"
);
await click(".poll-voters-toggle-expand:first a");
await click(".poll-voters-toggle-expand:nth-of-type(1) a");
assert.equal(
queryAll(".poll-voters:first li").length,
queryAll(".poll-voters:nth-of-type(1) li").length,
30,
"it should display the right number of voters"
);

View File

@ -30,20 +30,20 @@ acceptance("Rendering polls with bar charts - mobile", function (needs) {
await click("button.toggle-results");
assert.equal(
queryAll(".poll-voters:first li").length,
queryAll(".poll-voters:nth-of-type(1) li").length,
25,
"it should display the right number of voters"
);
assert.notOk(
queryAll(".poll-voters:first li:first a").attr("href"),
queryAll(".poll-voters:nth-of-type(1) li:nth-of-type(1) a").attr("href"),
"user URL does not exist"
);
await click(".poll-voters-toggle-expand:first a");
await click(".poll-voters-toggle-expand:nth-of-type(1) a");
assert.equal(
queryAll(".poll-voters:first li").length,
queryAll(".poll-voters:nth-of-type(1) li").length,
35,
"it should display the right number of voters"
);

File diff suppressed because it is too large Load Diff

View File

@ -2494,12 +2494,7 @@ pretender@^1.6:
fake-xml-http-request "^1.6.0"
route-recognizer "^0.3.3"
prettier@2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
prettier@^2.0.4:
prettier@2.2.1, prettier@^2.0.4:
version "2.2.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==