DEV: Improve tests and fixes small issues in the Glimmer Post Menu (#30234)

This commit improves some tests to using both the glimmer post menu and the widget version.

It also addresses some small issues in the Glimmer Post Menu:

- Deprecated Font Awesome icon in the Edit button
- Set correctly `aria-pressed` in the Like Count when the list of people who liked is visible
- Display the user tip for the Show More button
This commit is contained in:
Sérgio Saquetim 2024-12-12 15:36:05 -03:00 committed by GitHub
parent bbb31b05ca
commit 6fa52a6499
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 543 additions and 471 deletions

View File

@ -583,6 +583,7 @@ export default class PostMenu extends Component {
@outletArgs={{hash post=@post state=this.state}} @outletArgs={{hash post=@post state=this.state}}
> >
<nav <nav
{{! this.collapsed is included in the check below because "Show More" button can be overriden to be always visible }}
class={{concatClass class={{concatClass
"post-controls" "post-controls"
"glimmer-post-menu" "glimmer-post-menu"
@ -662,7 +663,11 @@ export default class PostMenu extends Component {
@users={{this.likedUsers}} @users={{this.likedUsers}}
/> />
{{/if}} {{/if}}
{{#if this.collapsedButtons}} {{#if
(this.showMoreButton.shouldRender
(hash post=this.post state=this.state)
)
}}
<UserTip <UserTip
@id="post_menu" @id="post_menu"
@triggerSelector=".post-controls .actions .show-more-actions" @triggerSelector=".post-controls .actions .show-more-actions"

View File

@ -35,7 +35,7 @@ export default class PostMenuEditButton extends Component {
}} }}
...attributes ...attributes
@action={{@buttonActions.editPost}} @action={{@buttonActions.editPost}}
@icon={{if @post.wiki "far-edit" "pencil"}} @icon={{if @post.wiki "far-pen-to-square" "pencil"}}
@label={{if this.showLabel "post.controls.edit_action"}} @label={{if this.showLabel "post.controls.edit_action"}}
@title="post.controls.edit" @title="post.controls.edit"
/> />

View File

@ -130,7 +130,7 @@ class LikeCount extends Component {
(if @post.yours "my-likes" "regular-likes") (if @post.yours "my-likes" "regular-likes")
}} }}
...attributes ...attributes
@ariaPressed={{@state.isWhoReadVisible}} @ariaPressed={{@state.isWhoLikedVisible}}
@translatedAriaLabel={{i18n @translatedAriaLabel={{i18n
"post.sr_post_like_count_button" "post.sr_post_like_count_button"
count=@post.likeCount count=@post.likeCount

View File

@ -3,7 +3,14 @@ import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers"; import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
acceptance("Post controls", function () { ["enabled", "disabled"].forEach((postMenuMode) => {
acceptance(
`Post controls (glimmer_post_menu_mode = ${postMenuMode})`,
function (needs) {
needs.settings({
glimmer_post_menu_mode: postMenuMode,
});
test("accessibility of the likes list below the post", async function (assert) { test("accessibility of the likes list below the post", async function (assert) {
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
@ -92,4 +99,6 @@ acceptance("Post controls", function () {
"collapse button has aria-label" "collapse button has aria-label"
); );
}); });
}
);
}); });

View File

@ -20,10 +20,15 @@ import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
import { cloneJSON } from "discourse-common/lib/object"; import { cloneJSON } from "discourse-common/lib/object";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
acceptance("Topic", function (needs) { ["enabled", "disabled"].forEach((postMenuMode) => {
acceptance(
`Topic (glimmer_post_menu_mode = ${postMenuMode})`,
function (needs) {
needs.user(); needs.user();
needs.settings({ needs.settings({
post_menu: "read|like|share|flag|edit|bookmark|delete|admin|reply|copyLink", post_menu:
"read|like|share|flag|edit|bookmark|delete|admin|reply|copyLink",
glimmer_post_menu_mode: postMenuMode,
}); });
needs.pretender((server, helper) => { needs.pretender((server, helper) => {
server.get("/c/2/visible_groups.json", () => server.get("/c/2/visible_groups.json", () =>
@ -91,7 +96,9 @@ acceptance("Topic", function (needs) {
test("Copy Link Button", async function (assert) { test("Copy Link Button", async function (assert) {
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
await click(".topic-post:first-child button.post-action-menu__copy-link"); await click(
".topic-post:first-child button.post-action-menu__copy-link"
);
assert assert
.dom(".post-action-menu__copy-link-checkmark") .dom(".post-action-menu__copy-link-checkmark")
@ -216,7 +223,7 @@ acceptance("Topic", function (needs) {
this.siteSettings.min_topic_views_for_delete_confirm = 10000; this.siteSettings.min_topic_views_for_delete_confirm = 10000;
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
await click(".topic-post:nth-of-type(1) button.show-more-actions"); await click(".topic-post:nth-of-type(1) button.show-more-actions");
await click(".widget-button.delete"); await click(".topic-post:nth-of-type(1) button.delete");
await click(".toggle-admin-menu"); await click(".toggle-admin-menu");
assert.dom(".topic-admin-recover").exists("shows the recover button"); assert.dom(".topic-admin-recover").exists("shows the recover button");
}); });
@ -225,7 +232,7 @@ acceptance("Topic", function (needs) {
this.siteSettings.min_topic_views_for_delete_confirm = 10; this.siteSettings.min_topic_views_for_delete_confirm = 10;
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
await click(".topic-post:nth-of-type(1) button.show-more-actions"); await click(".topic-post:nth-of-type(1) button.show-more-actions");
await click(".widget-button.delete"); await click(".topic-post:nth-of-type(1) button.delete");
assert assert
.dom(".delete-topic-confirm-modal") .dom(".delete-topic-confirm-modal")
.exists("shows the delete confirmation modal"); .exists("shows the delete confirmation modal");
@ -234,7 +241,7 @@ acceptance("Topic", function (needs) {
assert assert
.dom(".delete-topic-confirm-modal") .dom(".delete-topic-confirm-modal")
.doesNotExist("hides the delete confirmation modal"); .doesNotExist("hides the delete confirmation modal");
await click(".widget-button.delete"); await click(".topic-post:nth-of-type(1) button.delete");
await click(".delete-topic-confirm-modal .btn-danger"); await click(".delete-topic-confirm-modal .btn-danger");
await click(".toggle-admin-menu"); await click(".toggle-admin-menu");
assert.dom(".topic-admin-recover").exists("shows the recover button"); assert.dom(".topic-admin-recover").exists("shows the recover button");
@ -254,6 +261,8 @@ acceptance("Topic", function (needs) {
.dom(".topic-post.user-suspended > #post_1") .dom(".topic-post.user-suspended > #post_1")
.exists("has a class applied"); .exists("has a class applied");
}); });
}
);
}); });
acceptance("Topic featured links", function (needs) { acceptance("Topic featured links", function (needs) {

View File

@ -49,16 +49,26 @@ acceptance("User Tips - topic_timeline", function (needs) {
}); });
}); });
acceptance("User Tips - post_menu", function (needs) { ["enabled", "disabled"].forEach((postMenuMode) => {
acceptance(
`User Tips - post_menu (glimmer_post_menu_mode = ${postMenuMode})`,
function (needs) {
needs.user(); needs.user();
needs.site({ user_tips: { post_menu: 3 } }); needs.site({ user_tips: { post_menu: 3 } });
needs.settings({
glimmer_post_menu_mode: postMenuMode,
});
test("Shows post menu user tip", async function (assert) { test("Shows post menu user tip", async function (assert) {
this.siteSettings.enable_user_tips = true; this.siteSettings.enable_user_tips = true;
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
assert.dom(".user-tip__title").hasText(i18n("user_tips.post_menu.title")); assert
.dom(".user-tip__title")
.hasText(i18n("user_tips.post_menu.title"));
}); });
}
);
}); });
acceptance("User Tips - topic_notification_levels", function (needs) { acceptance("User Tips - topic_notification_levels", function (needs) {

View File

@ -0,0 +1,235 @@
import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit";
import DButton from "discourse/components/d-button";
import { withPluginApi } from "discourse/lib/plugin-api";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
function postStreamTest(name, attrs) {
test(name, async function (assert) {
this.set("posts", attrs.posts.call(this));
await render(
hbs`
<MountWidget @widget="post-stream" @args={{hash posts=this.posts}} />`
);
attrs.test.call(this, assert);
});
}
["enabled", "disabled"].forEach((postMenuMode) => {
let lastTransformedPost = null;
module(
`Integration | Component | Widget | post-stream (glimmer_post_menu_mode = ${postMenuMode})`,
function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.siteSettings.glimmer_post_menu_mode = postMenuMode;
});
hooks.afterEach(function () {
resetPostMenuExtraButtons();
});
const CustomPostMenuButton = <template>
<DButton
class="hot-coffee"
...attributes
@icon="mug-saucer"
@title="coffee.title"
/>
</template>;
postStreamTest("extensibility", {
posts() {
withPluginApi("1.34.0", (api) => {
api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { post, firstButtonKey } }) => {
dag.add("coffee", CustomPostMenuButton, {
before: firstButtonKey,
});
// value transformers shouldn't have side effects
// we are only doing it below for testing purposes. Do not use strategies like this in the app code
lastTransformedPost = post;
}
);
withSilencedDeprecations(
"discourse.post-menu-widget-overrides",
() => {
api.addPostMenuButton("coffee", (transformedPost) => {
lastTransformedPost = transformedPost;
return {
action: "drinkCoffee",
icon: "mug-saucer",
className: "hot-coffee",
title: "coffee.title",
position: "first",
};
});
}
);
});
const store = getOwner(this).lookup("service:store");
const topic = store.createRecord("topic");
topic.set("details.created_by", { id: 123 });
topic.set("id", 1234);
return [
store.createRecord("post", {
topic,
id: 1,
post_number: 1,
user_id: 123,
primary_group_name: "trout",
avatar_template: "/images/avatar.png",
}),
];
},
test(assert) {
assert.dom(".post-stream").exists({ count: 1 });
assert.dom(".topic-post").exists({ count: 1 }, "renders all posts");
assert
.dom(".topic-post:nth-of-type(1) button.hot-coffee")
.exists("it transforms posts");
assert.strictEqual(
lastTransformedPost.topic.id,
1234,
"it also transforms the topic"
);
assert
.dom(".actions .hot-coffee")
.exists({ count: 1 }, "has the extended button");
},
});
postStreamTest("basics", {
posts() {
const site = getOwner(this).lookup("service:site");
const store = getOwner(this).lookup("service:store");
const topic = store.createRecord("topic");
topic.set("details.created_by", { id: 123 });
return [
store.createRecord("post", {
topic,
id: 1,
post_number: 1,
user_id: 123,
primary_group_name: "trout",
avatar_template: "/images/avatar.png",
}),
store.createRecord("post", {
topic,
id: 2,
post_number: 2,
post_type: site.get("post_types.moderator_action"),
}),
store.createRecord("post", {
topic,
id: 3,
post_number: 3,
hidden: true,
}),
store.createRecord("post", {
topic,
id: 4,
post_number: 4,
post_type: site.get("post_types.whisper"),
}),
store.createRecord("post", {
topic,
id: 5,
post_number: 5,
wiki: true,
via_email: true,
}),
store.createRecord("post", {
topic,
id: 6,
post_number: 6,
via_email: true,
is_auto_generated: true,
}),
];
},
test(assert) {
assert.dom(".post-stream").exists({ count: 1 });
assert.dom(".topic-post").exists({ count: 6 }, "renders all posts");
// look for special class bindings
assert
.dom(".topic-post:nth-of-type(1).topic-owner")
.exists({ count: 1 }, "applies the topic owner class");
assert
.dom(".topic-post:nth-of-type(1).group-trout")
.exists({ count: 1 }, "applies the primary group class");
assert
.dom(".topic-post:nth-of-type(1).regular")
.exists({ count: 1 }, "applies the regular class");
assert
.dom(".topic-post:nth-of-type(2).moderator")
.exists({ count: 1 }, "applies the moderator class");
assert
.dom(".topic-post:nth-of-type(3).post-hidden")
.exists({ count: 1 }, "applies the hidden class");
assert
.dom(".topic-post:nth-of-type(4).whisper")
.exists({ count: 1 }, "applies the whisper class");
assert
.dom(".topic-post:nth-of-type(5).wiki")
.exists({ count: 1 }, "applies the wiki class");
// it renders an article for the body with appropriate attributes
assert.dom("article#post_2").exists({ count: 1 });
assert.dom('article[data-user-id="123"]').exists({ count: 1 });
assert.dom('article[data-post-id="3"]').exists({ count: 1 });
assert.dom("article#post_5.via-email").exists({ count: 1 });
assert.dom("article#post_6.is-auto-generated").exists({ count: 1 });
assert
.dom("article:nth-of-type(1) .main-avatar")
.exists({ count: 1 }, "renders the main avatar");
},
});
postStreamTest("deleted posts", {
posts() {
const store = getOwner(this).lookup("service:store");
const topic = store.createRecord("topic");
topic.set("details.created_by", { id: 123 });
return [
store.createRecord("post", {
topic,
id: 1,
post_number: 1,
deleted_at: new Date().toString(),
}),
];
},
test(assert) {
assert
.dom(".topic-post.deleted")
.exists({ count: 1 }, "applies the deleted class");
assert
.dom(".deleted-user-avatar")
.exists({ count: 1 }, "has the trash avatar");
},
});
}
);
});

View File

@ -1,196 +0,0 @@
import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit";
import { withPluginApi } from "discourse/lib/plugin-api";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
function postStreamTest(name, attrs) {
test(name, async function (assert) {
this.set("posts", attrs.posts.call(this));
await render(
hbs`<MountWidget @widget="post-stream" @args={{hash posts=this.posts}} />`
);
attrs.test.call(this, assert);
});
}
let lastTransformedPost = null;
module("Integration | Component | Widget | post-stream", function (hooks) {
setupRenderingTest(hooks);
hooks.afterEach(function () {
resetPostMenuExtraButtons();
});
postStreamTest("extensibility", {
posts() {
withPluginApi("0.14.0", (api) => {
withSilencedDeprecations("discourse.post-menu-widget-overrides", () => {
api.addPostMenuButton("coffee", (transformedPost) => {
lastTransformedPost = transformedPost;
return {
action: "drinkCoffee",
icon: "mug-saucer",
className: "hot-coffee",
title: "coffee.title",
position: "first",
};
});
});
});
const store = getOwner(this).lookup("service:store");
const topic = store.createRecord("topic");
topic.set("details.created_by", { id: 123 });
topic.set("id", 1234);
return [
store.createRecord("post", {
topic,
id: 1,
post_number: 1,
user_id: 123,
primary_group_name: "trout",
avatar_template: "/images/avatar.png",
}),
];
},
test(assert) {
assert.dom(".post-stream").exists({ count: 1 });
assert.dom(".topic-post").exists({ count: 1 }, "renders all posts");
assert.notStrictEqual(lastTransformedPost, null, "it transforms posts");
assert.strictEqual(
lastTransformedPost.topic.id,
1234,
"it also transforms the topic"
);
assert
.dom(".actions .extra-buttons .hot-coffee")
.exists({ count: 1 }, "has the extended button");
},
});
postStreamTest("basics", {
posts() {
const site = getOwner(this).lookup("service:site");
const store = getOwner(this).lookup("service:store");
const topic = store.createRecord("topic");
topic.set("details.created_by", { id: 123 });
return [
store.createRecord("post", {
topic,
id: 1,
post_number: 1,
user_id: 123,
primary_group_name: "trout",
avatar_template: "/images/avatar.png",
}),
store.createRecord("post", {
topic,
id: 2,
post_number: 2,
post_type: site.get("post_types.moderator_action"),
}),
store.createRecord("post", {
topic,
id: 3,
post_number: 3,
hidden: true,
}),
store.createRecord("post", {
topic,
id: 4,
post_number: 4,
post_type: site.get("post_types.whisper"),
}),
store.createRecord("post", {
topic,
id: 5,
post_number: 5,
wiki: true,
via_email: true,
}),
store.createRecord("post", {
topic,
id: 6,
post_number: 6,
via_email: true,
is_auto_generated: true,
}),
];
},
test(assert) {
assert.dom(".post-stream").exists({ count: 1 });
assert.dom(".topic-post").exists({ count: 6 }, "renders all posts");
// look for special class bindings
assert
.dom(".topic-post:nth-of-type(1).topic-owner")
.exists({ count: 1 }, "applies the topic owner class");
assert
.dom(".topic-post:nth-of-type(1).group-trout")
.exists({ count: 1 }, "applies the primary group class");
assert
.dom(".topic-post:nth-of-type(1).regular")
.exists({ count: 1 }, "applies the regular class");
assert
.dom(".topic-post:nth-of-type(2).moderator")
.exists({ count: 1 }, "applies the moderator class");
assert
.dom(".topic-post:nth-of-type(3).post-hidden")
.exists({ count: 1 }, "applies the hidden class");
assert
.dom(".topic-post:nth-of-type(4).whisper")
.exists({ count: 1 }, "applies the whisper class");
assert
.dom(".topic-post:nth-of-type(5).wiki")
.exists({ count: 1 }, "applies the wiki class");
// it renders an article for the body with appropriate attributes
assert.dom("article#post_2").exists({ count: 1 });
assert.dom('article[data-user-id="123"]').exists({ count: 1 });
assert.dom('article[data-post-id="3"]').exists({ count: 1 });
assert.dom("article#post_5.via-email").exists({ count: 1 });
assert.dom("article#post_6.is-auto-generated").exists({ count: 1 });
assert
.dom("article:nth-of-type(1) .main-avatar")
.exists({ count: 1 }, "renders the main avatar");
},
});
postStreamTest("deleted posts", {
posts() {
const store = getOwner(this).lookup("service:store");
const topic = store.createRecord("topic");
topic.set("details.created_by", { id: 123 });
return [
store.createRecord("post", {
topic,
id: 1,
post_number: 1,
deleted_at: new Date().toString(),
}),
];
},
test(assert) {
assert
.dom(".topic-post.deleted")
.exists({ count: 1 }, "applies the deleted class");
assert
.dom(".deleted-user-avatar")
.exists({ count: 1 }, "has the trash avatar");
},
});
});