import { module, test } from "qunit";
import { count, exists, query } from "discourse/tests/helpers/qunit-helpers";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { click, render, settled } from "@ember/test-helpers";
import { action } from "@ember/object";
import { extraConnectorClass } from "discourse/lib/plugin-connectors";
import { hbs } from "ember-cli-htmlbars";
import { registerTemporaryModule } from "discourse/tests/helpers/temporary-module-helper";
import { getOwner } from "discourse-common/lib/get-owner";
import Component from "@glimmer/component";
import templateOnly from "@ember/component/template-only";
import { withSilencedDeprecationsAsync } from "discourse-common/lib/deprecated";
const PREFIX = "discourse/plugins/some-plugin/templates/connectors";
module("Integration | Component | plugin-outlet", function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
extraConnectorClass("test-name/hello", {
actions: {
sayHello() {
this.set("hello", `${this.hello || ""}hello!`);
},
},
});
extraConnectorClass("test-name/hi", {
setupComponent() {
this.appEvents.on("hi:sayHi", this, this.say);
},
teardownComponent() {
this.appEvents.off("hi:sayHi", this, this.say);
},
@action
say() {
this.set("hi", "hi!");
},
@action
sayHi() {
this.appEvents.trigger("hi:sayHi");
},
});
extraConnectorClass("test-name/conditional-render", {
shouldRender(args, context) {
return args.shouldDisplay || context.siteSettings.always_display;
},
});
registerTemporaryModule(
`${PREFIX}/test-name/hello`,
hbs`{{username}}
{{hello}}`
);
registerTemporaryModule(
`${PREFIX}/test-name/hi`,
hbs`
{{hi}}`
);
registerTemporaryModule(
`${PREFIX}/test-name/conditional-render`,
hbs`I only render sometimes`
);
});
test("Renders a template into the outlet", async function (assert) {
this.set("shouldDisplay", false);
await render(
hbs``
);
assert.strictEqual(count(".hello-username"), 1, "renders the hello outlet");
assert.false(
exists(".conditional-render"),
"doesn't render conditional outlet"
);
await click(".say-hello");
assert.strictEqual(
query(".hello-result").innerText,
"hello!",
"actions delegate properly"
);
await click(".say-hello-using-this");
assert.strictEqual(
query(".hello-result").innerText,
"hello!hello!",
"actions are made available on `this` and are bound correctly"
);
await click(".say-hi");
assert.strictEqual(
query(".hi-result").innerText,
"hi!",
"actions delegate properly"
);
});
test("Reevaluates shouldRender for argument changes", async function (assert) {
this.set("shouldDisplay", false);
await render(
hbs``
);
assert.false(
exists(".conditional-render"),
"doesn't render conditional outlet"
);
this.set("shouldDisplay", true);
await settled();
assert.true(exists(".conditional-render"), "renders conditional outlet");
});
test("Reevaluates shouldRender for other autotracked changes", async function (assert) {
this.set("shouldDisplay", false);
await render(
hbs``
);
assert.false(
exists(".conditional-render"),
"doesn't render conditional outlet"
);
getOwner(this).lookup("service:site-settings").always_display = true;
await settled();
assert.true(exists(".conditional-render"), "renders conditional outlet");
});
test("Other outlets are not re-rendered", async function (assert) {
this.set("shouldDisplay", false);
await render(
hbs``
);
const otherOutletElement = query(".hello-username");
otherOutletElement.someUniqueProperty = true;
this.set("shouldDisplay", true);
await settled();
assert.true(exists(".conditional-render"), "renders conditional outlet");
assert.true(
query(".hello-username").someUniqueProperty,
"other outlet is left untouched"
);
});
});
module(
"Integration | Component | plugin-outlet | connector class definitions",
function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
registerTemporaryModule(
`${PREFIX}/test-name/my-connector`,
hbs`{{@outletArgs.hello}}{{this.hello}}`
);
});
test("uses classic PluginConnector by default", async function (assert) {
await render(
hbs``
);
assert.dom(".outletArgHelloValue").hasText("world");
assert.dom(".thisHelloValue").hasText("world");
});
test("uses templateOnly by default when @defaultGlimmer=true", async function (assert) {
await render(
hbs``
);
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);
extraConnectorClass("test-name/my-connector", {
shouldRender(args) {
return args.someBoolean;
},
setupComponent(args, component) {
component.reopen({
get hello() {
return args.hello + " from setupComponent";
},
});
},
});
await render(
hbs``
);
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) {
extraConnectorClass("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(
hbs``
);
}
);
assert.dom(".outletArgHelloValue").hasText("world");
assert.dom(".thisHelloValue").hasText("");
});
test("uses custom component class if provided", async function (assert) {
this.set("someBoolean", true);
extraConnectorClass(
"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``
);
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);
extraConnectorClass(
"test-name/my-connector",
Object.assign(templateOnly(), {
shouldRender(args) {
return args.someBoolean;
},
})
);
await render(
hbs``
);
assert.dom(".outletArgHelloValue").hasText("world");
assert.dom(".thisHelloValue").hasText(""); // `this.` unavailable in templateOnly components
this.set("someBoolean", false);
await settled();
assert.dom(".outletArgHelloValue").doesNotExist();
});
}
);