mirror of
https://github.com/hanzoai/forum.git
synced 2026-09-30 21:51:19 +00:00
1056 lines
33 KiB
Plaintext
1056 lines
33 KiB
Plaintext
import Component from "@glimmer/component";
|
|
import templateOnly from "@ember/component/template-only";
|
|
import { hash } from "@ember/helper";
|
|
import { getOwner } from "@ember/owner";
|
|
import { click, find, render, settled } from "@ember/test-helpers";
|
|
import hbs from "htmlbars-inline-precompile";
|
|
import { module, test } from "qunit";
|
|
import sinon from "sinon";
|
|
import PluginOutlet from "discourse/components/plugin-outlet";
|
|
import deprecatedOutletArgument from "discourse/helpers/deprecated-outlet-argument";
|
|
import deprecated, {
|
|
withSilencedDeprecations,
|
|
withSilencedDeprecationsAsync,
|
|
} from "discourse/lib/deprecated";
|
|
import {
|
|
extraConnectorClass,
|
|
extraConnectorComponent,
|
|
} from "discourse/lib/plugin-connectors";
|
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
import { registerTemporaryModule } from "discourse/tests/helpers/temporary-module-helper";
|
|
import {
|
|
disableRaiseOnDeprecation,
|
|
enableRaiseOnDeprecation,
|
|
} from "../../helpers/raise-on-deprecation";
|
|
|
|
const TEMPLATE_PREFIX = "discourse/plugins/some-plugin/templates/connectors";
|
|
const CLASS_PREFIX = "discourse/plugins/some-plugin/connectors";
|
|
|
|
module("Integration | Component | plugin-outlet", function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
registerTemporaryModule(`${CLASS_PREFIX}/test-name/hello`, {
|
|
actions: {
|
|
sayHello() {
|
|
this.set("hello", `${this.hello || ""}hello!`);
|
|
},
|
|
},
|
|
});
|
|
|
|
registerTemporaryModule(`${CLASS_PREFIX}/test-name/hi`, {
|
|
setupComponent() {
|
|
this.appEvents.on("hi:sayHi", this, this.say);
|
|
},
|
|
|
|
teardownComponent() {
|
|
this.appEvents.off("hi:sayHi", this, this.say);
|
|
},
|
|
|
|
actions: {
|
|
say() {
|
|
this.set("hi", "hi!");
|
|
},
|
|
|
|
sayHi() {
|
|
this.appEvents.trigger("hi:sayHi");
|
|
},
|
|
},
|
|
});
|
|
|
|
registerTemporaryModule(`${CLASS_PREFIX}/test-name/conditional-render`, {
|
|
shouldRender(args, context, owner) {
|
|
return (
|
|
args.shouldDisplay ||
|
|
context.siteSettings.always_display ||
|
|
owner.lookup("service:site-settings").alternativeAccess
|
|
);
|
|
},
|
|
});
|
|
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/test-name/hello`,
|
|
hbs`
|
|
<span class="hello-username">{{this.username}}</span>
|
|
<button
|
|
type="button"
|
|
class="say-hello"
|
|
{{on "click" (action "sayHello")}}
|
|
></button>
|
|
<button
|
|
type="button"
|
|
class="say-hello-using-this"
|
|
{{on "click" this.sayHello}}
|
|
></button>
|
|
<span class="hello-result">{{this.hello}}</span>
|
|
`
|
|
);
|
|
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/test-name/hi`,
|
|
hbs`
|
|
<button
|
|
type="button"
|
|
class="say-hi"
|
|
{{on "click" (action "sayHi")}}
|
|
></button>
|
|
<span class="hi-result">{{this.hi}}</span>
|
|
`
|
|
);
|
|
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/test-name/conditional-render`,
|
|
hbs`
|
|
<span class="conditional-render">I only render sometimes</span>
|
|
`
|
|
);
|
|
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default/my-connector`,
|
|
hbs`
|
|
<span class="result">Plugin implementation{{#if @outletArgs.yieldCore}}
|
|
{{yield}}{{/if}}</span>
|
|
`
|
|
);
|
|
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default/clashing-connector`,
|
|
hbs`This will override my-connector and raise an error`
|
|
);
|
|
});
|
|
|
|
test("Renders a template into the outlet", async function (assert) {
|
|
await render(hbs`<PluginOutlet @name="test-name" />`);
|
|
|
|
assert
|
|
.dom(".hello-username")
|
|
.exists({ count: 1 }, "renders the hello outlet");
|
|
assert
|
|
.dom(".conditional-render")
|
|
.doesNotExist("doesn't render conditional outlet");
|
|
|
|
await click(".say-hello");
|
|
assert.dom(".hello-result").hasText("hello!", "actions delegate properly");
|
|
|
|
await click(".say-hello-using-this");
|
|
assert
|
|
.dom(".hello-result")
|
|
.hasText(
|
|
"hello!hello!",
|
|
"actions are made available on `this` and are bound correctly"
|
|
);
|
|
|
|
await click(".say-hi");
|
|
assert.dom(".hi-result").hasText("hi!", "actions delegate properly");
|
|
});
|
|
|
|
module(
|
|
"as a wrapper around a default core implementation",
|
|
function (innerHooks) {
|
|
innerHooks.beforeEach(function () {
|
|
this.consoleErrorStub = sinon.stub(console, "error");
|
|
|
|
this.set("shouldDisplay", false);
|
|
this.set("yieldCore", false);
|
|
this.set("enableClashingConnector", false);
|
|
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/outlet-with-default/my-connector`,
|
|
{
|
|
shouldRender(args) {
|
|
return args.shouldDisplay;
|
|
},
|
|
}
|
|
);
|
|
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/outlet-with-default/clashing-connector`,
|
|
{
|
|
shouldRender(args) {
|
|
return args.enableClashingConnector;
|
|
},
|
|
}
|
|
);
|
|
|
|
this.template = hbs`
|
|
<PluginOutlet
|
|
@name="outlet-with-default"
|
|
@outletArgs={{hash
|
|
shouldDisplay=this.shouldDisplay
|
|
yieldCore=this.yieldCore
|
|
enableClashingConnector=this.enableClashingConnector
|
|
}}
|
|
>
|
|
<span class="result">Core implementation</span>
|
|
</PluginOutlet>
|
|
`;
|
|
});
|
|
|
|
test("Can act as a wrapper around core implementation", async function (assert) {
|
|
await render(this.template);
|
|
|
|
assert.dom(".result").hasText("Core implementation");
|
|
|
|
this.set("shouldDisplay", true);
|
|
await settled();
|
|
|
|
assert.dom(".result").hasText("Plugin implementation");
|
|
|
|
this.set("yieldCore", true);
|
|
await settled();
|
|
|
|
assert
|
|
.dom(".result")
|
|
.hasText("Plugin implementation Core implementation");
|
|
|
|
assert.strictEqual(
|
|
this.consoleErrorStub.callCount,
|
|
0,
|
|
"no errors in console"
|
|
);
|
|
});
|
|
|
|
test("clashing connectors for regular users", async function (assert) {
|
|
await render(this.template);
|
|
|
|
this.set("shouldDisplay", true);
|
|
this.set("enableClashingConnector", true);
|
|
await settled();
|
|
|
|
assert.strictEqual(
|
|
this.consoleErrorStub.callCount,
|
|
1,
|
|
"clash error reported to console"
|
|
);
|
|
|
|
assert.true(
|
|
this.consoleErrorStub
|
|
.getCall(0)
|
|
.args[0].includes("Multiple connectors"),
|
|
"console error includes message about multiple connectors"
|
|
);
|
|
|
|
assert
|
|
.dom(".broken-theme-alert-banner")
|
|
.doesNotExist("Banner is not shown to regular users");
|
|
});
|
|
|
|
test("clashing connectors for admins", async function (assert) {
|
|
this.set("currentUser.admin", true);
|
|
await render(this.template);
|
|
|
|
this.set("shouldDisplay", true);
|
|
this.set("enableClashingConnector", true);
|
|
await settled();
|
|
|
|
assert.strictEqual(
|
|
this.consoleErrorStub.callCount,
|
|
1,
|
|
"clash error reported to console"
|
|
);
|
|
|
|
assert.true(
|
|
this.consoleErrorStub
|
|
.getCall(0)
|
|
.args[0].includes("Multiple connectors"),
|
|
"console error includes message about multiple connectors"
|
|
);
|
|
|
|
assert
|
|
.dom(".broken-theme-alert-banner")
|
|
.exists("Error banner is shown to admins");
|
|
});
|
|
|
|
test("can render content in a automatic outlet generated before the wrapped content", async function (assert) {
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default__before/my-connector`,
|
|
hbs`
|
|
<span class="before-result">Before wrapped content</span>
|
|
`
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="outlet-with-default"
|
|
@outletArgs={{hash shouldDisplay=true}}
|
|
>
|
|
<span class="result">Core implementation</span>
|
|
</PluginOutlet>
|
|
`);
|
|
|
|
assert.dom(".result").hasText("Plugin implementation");
|
|
assert.dom(".before-result").hasText("Before wrapped content");
|
|
});
|
|
|
|
test("can render multiple connector `before` the same wrapped content", async function (assert) {
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default__before/my-connector`,
|
|
hbs`
|
|
<span class="before-result">First connector before the wrapped
|
|
content</span>
|
|
`
|
|
);
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default__before/my-connector2`,
|
|
hbs`
|
|
<span class="before-result2">Second connector before the wrapped
|
|
content</span>
|
|
`
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="outlet-with-default"
|
|
@outletArgs={{hash shouldDisplay=true}}
|
|
>
|
|
<span class="result">Core implementation</span>
|
|
</PluginOutlet>
|
|
`);
|
|
|
|
assert.dom(".result").hasText("Plugin implementation");
|
|
assert
|
|
.dom(".before-result")
|
|
.hasText("First connector before the wrapped content");
|
|
assert
|
|
.dom(".before-result2")
|
|
.hasText("Second connector before the wrapped content");
|
|
});
|
|
|
|
test("can render content in a automatic outlet generated after the wrapped content", async function (assert) {
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default__after/my-connector`,
|
|
hbs`
|
|
<span class="after-result">After wrapped content</span>
|
|
`
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="outlet-with-default"
|
|
@outletArgs={{hash shouldDisplay=true}}
|
|
>
|
|
<span class="result">Core implementation</span>
|
|
</PluginOutlet>
|
|
`);
|
|
|
|
assert.dom(".result").hasText("Plugin implementation");
|
|
assert.dom(".after-result").hasText("After wrapped content");
|
|
});
|
|
|
|
test("can render multiple connector `after` the same wrapped content", async function (assert) {
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default__after/my-connector`,
|
|
hbs`
|
|
<span class="after-result">First connector after the wrapped content</span>
|
|
`
|
|
);
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/outlet-with-default__after/my-connector2`,
|
|
hbs`
|
|
<span class="after-result2">Second connector after the wrapped
|
|
content</span>
|
|
`
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="outlet-with-default"
|
|
@outletArgs={{hash shouldDisplay=true}}
|
|
>
|
|
<span class="result">Core implementation</span>
|
|
</PluginOutlet>
|
|
`);
|
|
|
|
assert.dom(".result").hasText("Plugin implementation");
|
|
assert
|
|
.dom(".after-result")
|
|
.hasText("First connector after the wrapped content");
|
|
assert
|
|
.dom(".after-result2")
|
|
.hasText("Second connector after the wrapped content");
|
|
});
|
|
}
|
|
);
|
|
|
|
test("Renders wrapped implementation if no connectors are registered", async function (assert) {
|
|
await render(<template>
|
|
<PluginOutlet @name="outlet-with-no-registrations">
|
|
<span class="result">Core implementation</span>
|
|
</PluginOutlet>
|
|
</template>);
|
|
|
|
assert.dom(".result").hasText("Core implementation");
|
|
});
|
|
|
|
test("Reevaluates shouldRender for argument changes", async function (assert) {
|
|
this.set("shouldDisplay", false);
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash shouldDisplay=this.shouldDisplay}}
|
|
/>
|
|
`);
|
|
assert
|
|
.dom(".conditional-render")
|
|
.doesNotExist("doesn't render conditional outlet");
|
|
|
|
this.set("shouldDisplay", true);
|
|
await settled();
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
});
|
|
|
|
test("Reevaluates shouldRender for other autotracked changes", async function (assert) {
|
|
await render(hbs`<PluginOutlet @name="test-name" />`);
|
|
assert
|
|
.dom(".conditional-render")
|
|
.doesNotExist("doesn't render conditional outlet");
|
|
|
|
getOwner(this).lookup("service:site-settings").always_display = true;
|
|
await settled();
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
});
|
|
|
|
test("shouldRender receives an owner argument", async function (assert) {
|
|
await render(hbs`<PluginOutlet @name="test-name" />`);
|
|
assert
|
|
.dom(".conditional-render")
|
|
.doesNotExist("doesn't render conditional outlet");
|
|
|
|
getOwner(this).lookup("service:site-settings").alternativeAccess = true;
|
|
await settled();
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
});
|
|
|
|
test("Other outlets are not re-rendered", async function (assert) {
|
|
this.set("shouldDisplay", false);
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash shouldDisplay=this.shouldDisplay}}
|
|
/>
|
|
`);
|
|
|
|
find(".hello-username").someUniqueProperty = true;
|
|
|
|
this.set("shouldDisplay", true);
|
|
await settled();
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
|
|
assert
|
|
.dom(".hello-username")
|
|
.hasProperty(
|
|
"someUniqueProperty",
|
|
true,
|
|
"other outlet is left untouched"
|
|
);
|
|
});
|
|
|
|
module("deprecated arguments", function (innerHooks) {
|
|
innerHooks.beforeEach(function () {
|
|
this.consoleWarnStub = sinon.stub(console, "warn");
|
|
disableRaiseOnDeprecation();
|
|
});
|
|
|
|
innerHooks.afterEach(function () {
|
|
this.consoleWarnStub.restore();
|
|
enableRaiseOnDeprecation();
|
|
});
|
|
|
|
test("deprecated parameters with default message", async function (assert) {
|
|
await render(<template>
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@deprecatedArgs={{hash
|
|
shouldDisplay=(deprecatedOutletArgument value=true)
|
|
}}
|
|
/>
|
|
</template>);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
1,
|
|
"console warn was called once"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `shouldDisplay` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the default message to the console"
|
|
);
|
|
});
|
|
|
|
test("deprecated parameters with custom deprecation data", async function (assert) {
|
|
await render(<template>
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@deprecatedArgs={{hash
|
|
shouldDisplay=(deprecatedOutletArgument
|
|
value=true
|
|
message="The 'shouldDisplay' is deprecated on this test"
|
|
id="discourse.plugin-connector.deprecated-arg.test"
|
|
since="3.3.0.beta4-dev"
|
|
dropFrom="3.4.0"
|
|
)
|
|
}}
|
|
/>
|
|
</template>);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
1,
|
|
"console warn was called once"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
sinon.match(/The 'shouldDisplay' is deprecated on this test/)
|
|
),
|
|
"logs the custom deprecation message to the console"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
sinon.match(
|
|
/deprecation id: discourse.plugin-connector.deprecated-arg.test/
|
|
)
|
|
),
|
|
"logs custom deprecation id"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
sinon.match(/deprecated since Discourse 3.3.0.beta4-dev/)
|
|
),
|
|
"logs deprecation since information"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
sinon.match(/removal in Discourse 3.4.0/)
|
|
),
|
|
"logs dropFrom information"
|
|
);
|
|
});
|
|
|
|
test("silence nested deprecations", async function (assert) {
|
|
const deprecatedData = {
|
|
get display() {
|
|
deprecated("Test message", {
|
|
id: "discourse.deprecation-that-should-not-be-logged",
|
|
});
|
|
return true;
|
|
},
|
|
};
|
|
|
|
await render(<template>
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@deprecatedArgs={{hash
|
|
shouldDisplay=(deprecatedOutletArgument
|
|
value=deprecatedData.display
|
|
silence="discourse.deprecation-that-should-not-be-logged"
|
|
)
|
|
}}
|
|
/>
|
|
</template>);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
1,
|
|
"console warn was called once"
|
|
);
|
|
assert.false(
|
|
this.consoleWarnStub.calledWith(
|
|
sinon.match(
|
|
/deprecation id: discourse.deprecation-that-should-not-be-logged/
|
|
)
|
|
),
|
|
"does not log silence deprecation"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
sinon.match(
|
|
/deprecation id: discourse.plugin-connector.deprecated-arg/
|
|
)
|
|
),
|
|
"logs expected deprecation"
|
|
);
|
|
});
|
|
|
|
test("unused arguments", async function (assert) {
|
|
await render(<template>
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash shouldDisplay=true}}
|
|
@deprecatedArgs={{hash
|
|
argNotUsed=(deprecatedOutletArgument value=true)
|
|
}}
|
|
/>
|
|
</template>);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".conditional-render").exists("renders conditional outlet");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
0,
|
|
"console warn not called"
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
module(
|
|
"Integration | Component | plugin-outlet | connector class definitions",
|
|
function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/test-name/my-connector`,
|
|
hbs`
|
|
<span class="outletArgHelloValue">{{@outletArgs.hello}}</span>
|
|
<span class="thisHelloValue">{{this.hello}}</span>`
|
|
);
|
|
});
|
|
|
|
test("uses classic PluginConnector by default", async function (assert) {
|
|
await render(hbs`
|
|
<PluginOutlet @name="test-name" @outletArgs={{hash hello="world"}} />
|
|
`);
|
|
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world");
|
|
});
|
|
|
|
test("uses templateOnly by default when @defaultGlimmer=true", async function (assert) {
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash hello="world"}}
|
|
@defaultGlimmer={{true}}
|
|
/>
|
|
`);
|
|
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText(""); // `this.` unavailable in templateOnly components
|
|
});
|
|
|
|
test("uses simple object if provided", async function (assert) {
|
|
this.set("someBoolean", true);
|
|
|
|
registerTemporaryModule(`${CLASS_PREFIX}/test-name/my-connector`, {
|
|
shouldRender(args) {
|
|
return args.someBoolean;
|
|
},
|
|
|
|
setupComponent(args, component) {
|
|
component.reopen({
|
|
get hello() {
|
|
return args.hello + " from setupComponent";
|
|
},
|
|
});
|
|
},
|
|
});
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash hello="world" someBoolean=this.someBoolean}}
|
|
/>
|
|
`);
|
|
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world from setupComponent");
|
|
|
|
this.set("someBoolean", false);
|
|
await settled();
|
|
|
|
assert.dom(".outletArgHelloValue").doesNotExist();
|
|
});
|
|
|
|
test("ignores classic hooks for glimmer components", async function (assert) {
|
|
registerTemporaryModule(`${CLASS_PREFIX}/test-name/my-connector`, {
|
|
setupComponent(args, component) {
|
|
component.reopen({
|
|
get hello() {
|
|
return args.hello + " from setupComponent";
|
|
},
|
|
});
|
|
},
|
|
});
|
|
|
|
await withSilencedDeprecationsAsync(
|
|
"discourse.plugin-outlet-classic-hooks",
|
|
async () => {
|
|
await render(<template>
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash hello="world"}}
|
|
@defaultGlimmer={{true}}
|
|
/>
|
|
</template>);
|
|
}
|
|
);
|
|
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("");
|
|
});
|
|
|
|
test("uses custom component class if provided", async function (assert) {
|
|
this.set("someBoolean", true);
|
|
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/test-name/my-connector`,
|
|
class MyOutlet extends Component {
|
|
static shouldRender(args) {
|
|
return args.someBoolean;
|
|
}
|
|
|
|
get hello() {
|
|
return this.args.outletArgs.hello + " from custom component";
|
|
}
|
|
}
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash hello="world" someBoolean=this.someBoolean}}
|
|
/>
|
|
`);
|
|
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world from custom component");
|
|
|
|
this.set("someBoolean", false);
|
|
await settled();
|
|
|
|
assert.dom(".outletArgHelloValue").doesNotExist();
|
|
});
|
|
|
|
test("uses custom templateOnly() if provided", async function (assert) {
|
|
this.set("someBoolean", true);
|
|
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/test-name/my-connector`,
|
|
Object.assign(templateOnly(), {
|
|
shouldRender(args) {
|
|
return args.someBoolean;
|
|
},
|
|
})
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@outletArgs={{hash hello="world" someBoolean=this.someBoolean}}
|
|
/>
|
|
`);
|
|
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText(""); // `this.` unavailable in templateOnly components
|
|
|
|
this.set("someBoolean", false);
|
|
await settled();
|
|
|
|
assert.dom(".outletArgHelloValue").doesNotExist();
|
|
});
|
|
|
|
module("deprecated arguments", function (innerHooks) {
|
|
innerHooks.beforeEach(function () {
|
|
this.consoleWarnStub = sinon.stub(console, "warn");
|
|
disableRaiseOnDeprecation();
|
|
});
|
|
|
|
innerHooks.afterEach(function () {
|
|
this.consoleWarnStub.restore();
|
|
enableRaiseOnDeprecation();
|
|
});
|
|
|
|
test("using classic PluginConnector by default", async function (assert) {
|
|
await render(hbs`
|
|
<PluginOutlet @name="test-name" @deprecatedArgs={{hash hello=(deprecated-outlet-argument value="world")}} />
|
|
`);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
2,
|
|
"console warn was called twice"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for @outletArgs.hello"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [used on connector discourse/plugins/some-plugin/templates/connectors/test-name/my-connector] [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for this.hello"
|
|
);
|
|
});
|
|
|
|
test("using templateOnly by default when @defaultGlimmer=true", async function (assert) {
|
|
await render(hbs`
|
|
<PluginOutlet
|
|
@name="test-name"
|
|
@deprecatedArgs={{hash hello=(deprecated-outlet-argument value="world")}}
|
|
@defaultGlimmer={{true}}
|
|
/>
|
|
`);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText(""); // `this.` unavailable in templateOnly components
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
1,
|
|
"console warn was called once"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for @outletArgs.hello"
|
|
);
|
|
assert.false(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [used on connector discourse/plugins/some-plugin/templates/connectors/test-name/my-connector] [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"does not log the message for this.hello"
|
|
);
|
|
});
|
|
|
|
test("using simple object when provided", async function (assert) {
|
|
registerTemporaryModule(`${CLASS_PREFIX}/test-name/my-connector`, {
|
|
setupComponent(args, component) {
|
|
component.reopen({
|
|
get hello() {
|
|
return args.hello + " from setupComponent";
|
|
},
|
|
});
|
|
},
|
|
});
|
|
|
|
await render(hbs`
|
|
<PluginOutlet @name="test-name" @deprecatedArgs={{hash hello=(deprecated-outlet-argument value="world")}} />
|
|
`);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world from setupComponent");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
2,
|
|
"console warn was called twice"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for @outletArgs.hello"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [used on connector discourse/plugins/some-plugin/connectors/test-name/my-connector] [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for this.hello"
|
|
);
|
|
});
|
|
|
|
test("using custom component class if provided", async function (assert) {
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/test-name/my-connector`,
|
|
class MyOutlet extends Component {
|
|
get hello() {
|
|
return this.args.outletArgs.hello + " from custom component";
|
|
}
|
|
}
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet @name="test-name" @deprecatedArgs={{hash hello=(deprecated-outlet-argument value="world")}} />
|
|
`);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world from custom component");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
2,
|
|
"console warn was called twice"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for @outletArgs.hello"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for this.hello"
|
|
);
|
|
});
|
|
|
|
test("using custom templateOnly() if provided", async function (assert) {
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/test-name/my-connector`,
|
|
templateOnly()
|
|
);
|
|
|
|
await render(hbs`
|
|
<PluginOutlet @name="test-name" @deprecatedArgs={{hash hello=(deprecated-outlet-argument value="world")}} />
|
|
`);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText(""); // `this.` unavailable in templateOnly components
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
1,
|
|
"console warn was called twice"
|
|
);
|
|
assert.true(
|
|
this.consoleWarnStub.calledWith(
|
|
"Deprecation notice: outlet arg `hello` is deprecated on the outlet `test-name` [deprecation id: discourse.plugin-connector.deprecated-arg]"
|
|
),
|
|
"logs the expected message for @outletArgs.hello"
|
|
);
|
|
});
|
|
|
|
test("unused arguments", async function (assert) {
|
|
await render(hbs`
|
|
<PluginOutlet @name="test-name" @outletArgs={{hash hello="world"}} @deprecatedArgs={{hash argNotUsed=(deprecated-outlet-argument value="not used")}} />
|
|
`);
|
|
|
|
// deprecated argument still works
|
|
assert.dom(".outletArgHelloValue").hasText("world");
|
|
assert.dom(".thisHelloValue").hasText("world");
|
|
|
|
assert.strictEqual(
|
|
this.consoleWarnStub.callCount,
|
|
0,
|
|
"console warn was called twice"
|
|
);
|
|
});
|
|
});
|
|
}
|
|
);
|
|
|
|
module(
|
|
"Integration | Component | plugin-outlet | gjs class definitions",
|
|
function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
registerTemporaryModule(
|
|
`${CLASS_PREFIX}/test-name/my-connector`,
|
|
<template><span class="gjs-test">Hello world</span></template>
|
|
);
|
|
});
|
|
|
|
test("detects a gjs connector with no associated template file", async function (assert) {
|
|
await render(<template><PluginOutlet @name="test-name" /></template>);
|
|
|
|
assert.dom(".gjs-test").hasText("Hello world");
|
|
});
|
|
}
|
|
);
|
|
|
|
module(
|
|
"Integration | Component | plugin-outlet | extraConnectorComponent",
|
|
function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
extraConnectorComponent("test-name", <template>
|
|
<span class="gjs-test">Hello world from gjs</span>
|
|
</template>);
|
|
});
|
|
|
|
test("renders the component in the outlet", async function (assert) {
|
|
await render(<template><PluginOutlet @name="test-name" /></template>);
|
|
assert.dom(".gjs-test").hasText("Hello world from gjs");
|
|
});
|
|
|
|
test("throws errors for invalid components", function (assert) {
|
|
assert.throws(() => {
|
|
extraConnectorComponent("test-name/blah", <template>
|
|
hello world
|
|
</template>);
|
|
}, /invalid outlet name/);
|
|
|
|
assert.throws(() => {
|
|
extraConnectorComponent("test-name", {});
|
|
}, /klass is not an Ember component/);
|
|
|
|
assert.throws(() => {
|
|
extraConnectorComponent("test-name", class extends Component {});
|
|
}, /connector component has no associated template/);
|
|
});
|
|
}
|
|
);
|
|
|
|
module("Integration | Component | plugin-outlet | tagName", function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
test("supports the `@tagName` argument", async function (assert) {
|
|
await withSilencedDeprecationsAsync(
|
|
"discourse.plugin-outlet-tag-name",
|
|
async () =>
|
|
await render(<template>
|
|
<PluginOutlet @name="test-name" @tagName="div" />
|
|
</template>)
|
|
);
|
|
assert.dom("div").exists();
|
|
});
|
|
});
|
|
|
|
module(
|
|
"Integration | Component | plugin-outlet | legacy extraConnectorClass",
|
|
function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
registerTemporaryModule(
|
|
`${TEMPLATE_PREFIX}/test-name/my-legacy-connector`,
|
|
hbs`
|
|
<span class="legacy-test">Hello world {{this.someVar}}</span>
|
|
`
|
|
);
|
|
|
|
withSilencedDeprecations(
|
|
"discourse.register-connector-class-legacy",
|
|
() =>
|
|
extraConnectorClass("test-name/my-legacy-connector", {
|
|
setupComponent(outletArgs, component) {
|
|
component.set("someVar", "from legacy");
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
test("links up template with extra connector class", async function (assert) {
|
|
await render(hbs`<PluginOutlet @name="test-name" />`);
|
|
assert.dom(".legacy-test").hasText("Hello world from legacy");
|
|
});
|
|
}
|
|
);
|