This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/tests/unit/ember/resolver-test.js
David Taylor c139767055
DEV: Remove Ember.TEMPLATES and centralize template resolution rules (#19220)
In the past, the result of template compilation would be stored directly in `Ember.TEMPLATES`. Following the move to more modern ember-cli-based compilation, templates are now compiled to es6 modules. To handle forward/backwards compatibility during these changes we had logic in `discourse-boot` which would extract templates from the es6 modules and store them into the legacy-style `Ember.TEMPLATES` object.

This commit removes that shim, and updates our resolver to fetch templates directly from es6 modules. This is closer to how 'vanilla' Ember handles template resolution. We still have a lot of discourse-specific logic, but now it is centralised in one location and should be easier to understand and normalize in future.

This commit should not introduce any behaviour change.
2022-11-29 10:24:35 +00:00

684 lines
18 KiB
JavaScript

import { buildResolver, setResolverOption } from "discourse-common/resolver";
import { module, test } from "qunit";
import { registerTemplateModule } from "discourse/tests/helpers/template-module-helper";
import DiscourseTemplateMap from "discourse-common/lib/discourse-template-map";
let resolver;
function lookupTemplate(assert, name, expectedTemplate, message) {
let parseName = resolver.parseName(name);
let result = resolver.resolveTemplate(parseName);
assert.strictEqual(result, expectedTemplate, message);
}
function setTemplates(templateModuleNames) {
for (const name of templateModuleNames) {
registerTemplateModule(name, name);
}
}
const DiscourseResolver = buildResolver("discourse");
module("Unit | Ember | resolver", function (hooks) {
hooks.beforeEach(function () {
DiscourseTemplateMap.setModuleNames(Object.keys(requirejs.entries));
resolver = DiscourseResolver.create({
namespace: { modulePrefix: "discourse" },
});
});
test("finds templates in top level dir", function (assert) {
setTemplates([
"discourse/templates/foobar",
"discourse/templates/fooBar",
"discourse/templates/foo_bar",
"discourse/templates/foo.bar",
]);
// Default unmodified behavior
lookupTemplate(
assert,
"template:foobar",
"discourse/templates/foobar",
"by lowcased name"
);
// Default unmodified behavior
lookupTemplate(
assert,
"template:fooBar",
"discourse/templates/fooBar",
"by camel cased name"
);
// Default unmodified behavior
lookupTemplate(
assert,
"template:foo_bar",
"discourse/templates/foo_bar",
"by underscored name"
);
// Default unmodified behavior
lookupTemplate(
assert,
"template:foo.bar",
"discourse/templates/foo.bar",
"by dotted name"
);
});
test("finds templates in first-level subdir", function (assert) {
setTemplates(["discourse/templates/foo/bar_baz"]);
// Default unmodified behavior
lookupTemplate(
assert,
"template:foo/bar_baz",
"discourse/templates/foo/bar_baz",
"with subdir defined by slash"
);
// Convert dots to slash
lookupTemplate(
assert,
"template:foo.bar_baz",
"discourse/templates/foo/bar_baz",
"with subdir defined by dot"
);
// Convert dashes to slash
lookupTemplate(
assert,
"template:foo-bar_baz",
"discourse/templates/foo/bar_baz",
"with subdir defined by dash"
);
// Underscored with first segment as directory
lookupTemplate(
assert,
"template:fooBarBaz",
"discourse/templates/foo/bar_baz",
"with subdir defined by first camel case and the rest of camel cases converted to underscores"
);
// Already underscored with first segment as directory
lookupTemplate(
assert,
"template:foo_bar_baz",
"discourse/templates/foo/bar_baz",
"with subdir defined by first underscore"
);
});
test("resolves precedence between overlapping top level dir and first level subdir templates", function (assert) {
setTemplates([
"discourse/templates/fooBar",
"discourse/templates/foo_bar",
"discourse/templates/foo.bar",
"discourse/templates/foo/bar",
"discourse/templates/baz/qux",
]);
// Directories are prioritized when dotted
lookupTemplate(
assert,
"template:foo.bar",
"discourse/templates/foo/bar",
"preferring first level subdir for dotted name"
);
// Directories are prioritized when dashed
lookupTemplate(
assert,
"template:foo-bar",
"discourse/templates/foo/bar",
"preferring first level subdir for dotted name"
);
// Default unmodified before directories, except when dotted
lookupTemplate(
assert,
"template:fooBar",
"discourse/templates/fooBar",
"preferring top level dir for camel cased name"
);
// Default unmodified before directories, except when dotted
lookupTemplate(
assert,
"template:foo_bar",
"discourse/templates/foo_bar",
"preferring top level dir for underscored name"
);
// Use directory version if top-level isn't found
lookupTemplate(
assert,
"template:baz-qux",
"discourse/templates/baz/qux",
"fallback subdir for dashed name"
);
});
test("finds templates in subdir deeper than one level", function (assert) {
setTemplates(["discourse/templates/foo/bar/baz/qux"]);
// Default unmodified
lookupTemplate(
assert,
"template:foo/bar/baz/qux",
"discourse/templates/foo/bar/baz/qux",
"for subdirs defined by slashes"
);
// Converts dotted to slashed
lookupTemplate(
assert,
"template:foo.bar.baz.qux",
"discourse/templates/foo/bar/baz/qux",
"for subdirs defined by dots"
);
// Converts first camelized segment to slashed
lookupTemplate(
assert,
"template:foo/bar/bazQux",
"discourse/templates/foo/bar/baz/qux",
"for subdirs defined by slashes plus one camel case"
);
// Converts first underscore to slashed
lookupTemplate(
assert,
"template:foo/bar/baz_qux",
"discourse/templates/foo/bar/baz/qux",
"for subdirs defined by slashes plus one underscore"
);
// Only converts first camelized segment to slashed so this isn't matched
lookupTemplate(
assert,
"template:fooBarBazQux",
undefined,
"but not for subdirs defined by more than one camel case"
);
// Only converts first underscored segment to slashed so this isn't matched
lookupTemplate(
assert,
"template:foo_bar_baz_qux",
undefined,
"but not for subdirs defined by more than one underscore"
);
// Only converts dots to slashes OR first camelized segment. This has both so isn't matched.
lookupTemplate(
assert,
"template:foo.bar.bazQux",
undefined,
"but not for subdirs defined by dots plus one camel case"
);
// Only converts dots to slashes OR first underscored segment. This has both so isn't matched.
lookupTemplate(
assert,
"template:foo.bar.baz_qux",
undefined,
"but not for subdirs defined by dots plus one underscore"
);
});
test("resolves mobile templates to 'mobile/' namespace", function (assert) {
setTemplates([
"discourse/templates/mobile/foo",
"discourse/templates/bar",
"discourse/templates/mobile/bar",
"discourse/templates/baz",
]);
setResolverOption("mobileView", true);
// Default with mobile/ added
lookupTemplate(
assert,
"template:foo",
"discourse/templates/mobile/foo",
"finding mobile version even if normal one is not present"
);
// Default with mobile preferred
lookupTemplate(
assert,
"template:bar",
"discourse/templates/mobile/bar",
"preferring mobile version when both mobile and normal versions are present"
);
// Default when mobile not present
lookupTemplate(
assert,
"template:baz",
"discourse/templates/baz",
"falling back to a normal version when mobile version is not present"
);
});
test("resolves templates to plugin and theme namespaces", function (assert) {
setTemplates([
"discourse/plugins/my-plugin/discourse/templates/foo",
"discourse/templates/bar",
"discourse/plugins/my-plugin/discourse/templates/bar",
"discourse/templates/baz",
"discourse/plugins/my-plugin/discourse/templates/baz",
"discourse/theme-12/discourse/templates/baz",
"discourse/templates/qux",
]);
// Defined in plugin only
lookupTemplate(
assert,
"template:foo",
"discourse/plugins/my-plugin/discourse/templates/foo",
"finding plugin version even if normal one is not present"
);
// Defined in core and plugin
lookupTemplate(
assert,
"template:bar",
"discourse/plugins/my-plugin/discourse/templates/bar",
"prefers plugin version over core"
);
// Defined in core and plugin and theme
lookupTemplate(
assert,
"template:baz",
"discourse/theme-12/discourse/templates/baz",
"prefers theme version over plugin and core"
);
// Defined in core only
lookupTemplate(
assert,
"template:qux",
"discourse/templates/qux",
"uses core if there are no theme/plugin definitions"
);
});
test("resolves plugin mobile templates", function (assert) {
setTemplates([
"discourse/plugins/my-plugin/discourse/templates/mobile/foo",
"discourse/plugins/my-plugin/discourse/templates/mobile/bar",
"discourse/plugins/my-plugin/discourse/templates/bar",
"discourse/plugins/my-plugin/discourse/templates/mobile/baz",
"discourse/templates/mobile/baz",
]);
setResolverOption("mobileView", true);
// Default with plugin template override
lookupTemplate(
assert,
"template:foo",
"discourse/plugins/my-plugin/discourse/templates/mobile/foo",
"finding plugin version even if normal one is not present"
);
// Default with plugin mobile added, takes precedence over non-mobile
lookupTemplate(
assert,
"template:bar",
"discourse/plugins/my-plugin/discourse/templates/mobile/bar",
"preferring plugin mobile version when both non-mobile plugin version is also present"
);
// Default with when non-plugin mobile version is present
lookupTemplate(
assert,
"template:baz",
"discourse/plugins/my-plugin/discourse/templates/mobile/baz",
"preferring plugin mobile version over non-plugin mobile version"
);
});
test("resolves templates with 'admin' prefix", function (assert) {
setTemplates([
"admin/templates/foo",
"discourse/templates/adminBar",
"discourse/templates/admin_bar",
"discourse/templates/admin.bar",
"admin/templates/bar",
"admin/templates/dashboard_general",
"discourse/templates/admin-baz-qux",
"discourse/plugins/my-plugin/discourse/templates/admin/plugin-template",
"admin/templates/components/my-admin-component",
]);
// Switches prefix to admin/templates when camelized
lookupTemplate(
assert,
"template:adminFoo",
"admin/templates/foo",
"when prefix is separated by camel case"
);
// Switches prefix to admin/templates when underscored
lookupTemplate(
assert,
"template:admin_foo",
"admin/templates/foo",
"when prefix is separated by underscore"
);
// Switches prefix to admin/templates when dotted
lookupTemplate(
assert,
"template:admin.foo",
"admin/templates/foo",
"when prefix is separated by dot"
);
// Doesn't match unseparated prefix
lookupTemplate(
assert,
"template:adminfoo",
undefined,
"but not when prefix is not separated in any way"
);
// Prioritized the default match when camelized
lookupTemplate(
assert,
"template:adminBar",
"discourse/templates/adminBar",
"but not when template with the exact camel cased name exists"
);
// Prioritized the default match when underscored
lookupTemplate(
assert,
"template:admin_bar",
"discourse/templates/admin_bar",
"but not when template with the exact underscored name exists"
);
// Prioritized the default match when dotted
lookupTemplate(
assert,
"template:admin.bar",
"discourse/templates/admin.bar",
"but not when template with the exact dotted name exists"
);
lookupTemplate(
assert,
"template:admin-dashboard-general",
"admin/templates/dashboard_general",
"finds namespaced and underscored version"
);
lookupTemplate(
assert,
"template:admin-baz/qux",
"discourse/templates/admin-baz-qux",
"also tries dasherized"
);
lookupTemplate(
assert,
"template:admin-plugin/template",
"discourse/plugins/my-plugin/discourse/templates/admin/plugin-template",
"looks up templates in plugins"
);
lookupTemplate(
assert,
"template:foo",
undefined,
"doesn't return admin templates for regular controllers"
);
lookupTemplate(
assert,
"template:components/my-admin-component",
"admin/templates/components/my-admin-component",
"returns admin-defined component templates"
);
});
test("resolves component templates with 'admin' prefix to 'admin/templates/' namespace", function (assert) {
setTemplates([
"admin/templates/components/foo",
"discourse/templates/components/bar",
"admin/templates/components/bar",
]);
// Looks for components in admin/templates
lookupTemplate(
assert,
"template:components/foo",
"admin/templates/components/foo",
"uses admin template component when no standard match"
);
// Prioritized non-admin component
lookupTemplate(
assert,
"template:components/bar",
"discourse/templates/components/bar",
"uses standard match when both exist"
);
});
// We can probably remove this in the future since this behavior seems pretty
// close to Ember's default behavior.
// See https://guides.emberjs.com/release/routing/loading-and-error-substates/
test("resolves loading templates", function (assert) {
setTemplates([
"discourse/templates/fooloading",
"discourse/templates/foo/loading",
"discourse/templates/foo_loading",
"discourse/templates/loading",
]);
lookupTemplate(
assert,
"template:fooloading",
"discourse/templates/fooloading",
"exact match without separator"
);
lookupTemplate(
assert,
"template:foo/loading",
"discourse/templates/foo/loading",
"exact match with slash"
);
lookupTemplate(
assert,
"template:foo_loading",
"discourse/templates/foo_loading",
"exact match underscore"
);
lookupTemplate(
assert,
"template:barloading",
"discourse/templates/loading",
"fallback without separator"
);
lookupTemplate(
assert,
"template:bar/loading",
"discourse/templates/loading",
"fallback with slash"
);
lookupTemplate(
assert,
"template:bar.loading",
"discourse/templates/loading",
"fallback with dot"
);
lookupTemplate(
assert,
"template:bar_loading",
"discourse/templates/loading",
"fallback underscore"
);
// TODO: Maybe test precedence
});
test("resolves connector templates", function (assert) {
setTemplates([
"discourse/plugins/my-plugin/discourse/templates/foo",
"discourse/plugins/my-plugin/discourse/templates/connectors/foo-bar/baz_qux",
"discourse/plugins/my-plugin/discourse/templates/connectors/foo-bar/camelCase",
]);
lookupTemplate(
assert,
"template:connectors/foo",
"discourse/plugins/my-plugin/discourse/templates/foo",
"looks up in plugin namespace"
);
lookupTemplate(
assert,
"template:connectors/components/foo",
"discourse/plugins/my-plugin/discourse/templates/foo",
"removes components segment"
);
lookupTemplate(
assert,
"template:connectors/foo-bar/baz-qux",
"discourse/plugins/my-plugin/discourse/templates/connectors/foo-bar/baz_qux",
"underscores last segment"
);
lookupTemplate(
assert,
"template:connectors/foo-bar/camelCase",
"discourse/plugins/my-plugin/discourse/templates/connectors/foo-bar/camelCase",
"handles camelcase file names"
);
lookupTemplate(
assert,
resolver.normalize("template:connectors/foo-bar/camelCase"),
"discourse/plugins/my-plugin/discourse/templates/connectors/foo-bar/camelCase",
"handles camelcase file names when normalized"
);
});
test("returns 'not_found' template when template name cannot be resolved", function (assert) {
setTemplates(["discourse/templates/not_found"]);
lookupTemplate(
assert,
"template:foo/bar/baz",
"discourse/templates/not_found",
""
);
});
test("resolves templates with 'wizard' prefix", function (assert) {
setTemplates([
"wizard/templates/foo",
"discourse/templates/wizard_bar",
"discourse/templates/wizard.bar",
"wizard/templates/bar",
"wizard/templates/dashboard_general",
"discourse/templates/wizard-baz-qux",
"javascripts/wizard/plugin-template",
]);
// Switches prefix to wizard/templates when underscored
lookupTemplate(
assert,
"template:wizard_foo",
"wizard/templates/foo",
"when prefix is separated by underscore"
);
// Switches prefix to wizard/templates when dotted
lookupTemplate(
assert,
"template:wizard.foo",
"wizard/templates/foo",
"when prefix is separated by dot"
);
// Doesn't match unseparated prefix
lookupTemplate(
assert,
"template:wizardfoo",
undefined,
"but not when prefix is not separated in any way"
);
// Prioritized the default match when underscored
lookupTemplate(
assert,
"template:wizard_bar",
"discourse/templates/wizard_bar",
"but not when template with the exact underscored name exists"
);
// Prioritized the default match when dotted
lookupTemplate(
assert,
"template:wizard.bar",
"discourse/templates/wizard.bar",
"but not when template with the exact dotted name exists"
);
lookupTemplate(
assert,
"template:wizard-dashboard-general",
"wizard/templates/dashboard_general",
"finds namespaced and underscored version"
);
lookupTemplate(
assert,
"template:wizard-baz/qux",
"discourse/templates/wizard-baz-qux",
"also tries dasherized"
);
});
test("resolves component templates with 'wizard' prefix to 'wizard/templates/' namespace", function (assert) {
setTemplates([
"wizard/templates/components/foo",
"discourse/templates/components/bar",
"wizard/templates/components/bar",
]);
// Looks for components in wizard/templates
lookupTemplate(
assert,
"template:components/foo",
"wizard/templates/components/foo",
"uses wizard template component when no standard match"
);
// Prioritized non-wizard component
lookupTemplate(
assert,
"template:components/bar",
"discourse/templates/components/bar",
"uses standard match when both exist"
);
});
});