diff --git a/app/assets/javascripts/discourse/app/components/tap-tile.js b/app/assets/javascripts/discourse/app/components/tap-tile.js
index 3a5203573d0..370415d826c 100644
--- a/app/assets/javascripts/discourse/app/components/tap-tile.js
+++ b/app/assets/javascripts/discourse/app/components/tap-tile.js
@@ -2,6 +2,10 @@ import Component from "@ember/component";
import { propertyEqual } from "discourse/lib/computed";
export default Component.extend({
+ init() {
+ this._super(...arguments);
+ this.set("elementId", `tap_tile_${this.tileId}`);
+ },
classNames: ["tap-tile"],
classNameBindings: ["active"],
click() {
diff --git a/app/assets/javascripts/discourse/app/templates/modal/bookmark.hbs b/app/assets/javascripts/discourse/app/templates/modal/bookmark.hbs
index 3a5075ebc9e..d9e51fbbefc 100644
--- a/app/assets/javascripts/discourse/app/templates/modal/bookmark.hbs
+++ b/app/assets/javascripts/discourse/app/templates/modal/bookmark.hbs
@@ -71,11 +71,12 @@
{{date-picker-future
value=customReminderDate
onSelect=(action (mut customReminderDate))
+ id="bookmark-custom-date"
}}
{{d-icon "far-clock"}}
- {{input placeholder="--:--" type="time" class="time-input" value=customReminderTime}}
+ {{input placeholder="--:--" id="bookmark-custom-time" type="time" class="time-input" value=customReminderTime}}
{{/if}}
@@ -98,11 +99,11 @@
- {{d-button label="bookmarks.save" class="btn-primary" action=(action "saveAndClose")}}
+ {{d-button id="save-bookmark" label="bookmarks.save" class="btn-primary" action=(action "saveAndClose")}}
{{d-modal-cancel close=(action "closeWithoutSavingBookmark")}}
{{#if showDelete}}
- {{d-button icon="trash" class="btn-danger" action=(action "delete")}}
+ {{d-button id="delete-bookmark" icon="trash" class="btn-danger" action=(action "delete")}}
{{/if}}
diff --git a/test/javascripts/acceptance/bookmarks-test.js b/test/javascripts/acceptance/bookmarks-test.js
new file mode 100644
index 00000000000..2c2008f3e2d
--- /dev/null
+++ b/test/javascripts/acceptance/bookmarks-test.js
@@ -0,0 +1,161 @@
+import { acceptance, loggedInUser } from "helpers/qunit-helpers";
+import pretender from "helpers/create-pretender";
+
+acceptance("Bookmarking", {
+ loggedIn: true,
+
+ beforeEach() {}
+});
+
+function mockSuccessfulBookmarkPost() {
+ pretender.post("/bookmarks", () => [
+ 200,
+ {
+ "Content-Type": "application/json"
+ },
+ {
+ id: 999,
+ success: "OK"
+ }
+ ]);
+}
+
+async function openBookmarkModal() {
+ await click(".topic-post:first-child button.show-more-actions");
+ return await click(".topic-post:first-child button.bookmark");
+}
+async function openEditBookmarkModal() {
+ return await click(".topic-post:first-child button.bookmarked");
+}
+
+test("Bookmarks modal opening", async assert => {
+ await visit("/t/internationalization-localization/280");
+ await openBookmarkModal();
+ assert.ok(exists("#bookmark-reminder-modal"), "it shows the bookmark modal");
+});
+
+test("Bookmarks modal selecting reminder type", async assert => {
+ await visit("/t/internationalization-localization/280");
+ await openBookmarkModal();
+ await click("#tap_tile_tomorrow");
+ assert.ok(exists("#tap_tile_tomorrow.active"), "it selects tomorrow");
+ await click("#tap_tile_start_of_next_business_week");
+ assert.ok(
+ exists("#tap_tile_start_of_next_business_week.active"),
+ "it selects next monday"
+ );
+ await click("#tap_tile_next_week");
+ assert.ok(exists("#tap_tile_next_week.active"), "it selects next week");
+ await click("#tap_tile_next_month");
+ assert.ok(exists("#tap_tile_next_month.active"), "it selects next month");
+ await click("#tap_tile_custom");
+ assert.ok(exists("#tap_tile_custom.active"), "it selects custom");
+ assert.ok(exists(".tap-tile-date-input"), "it shows the custom date input");
+ assert.ok(exists(".tap-tile-time-input"), "it shows the custom time input");
+});
+
+test("Saving a bookmark with a reminder", async assert => {
+ mockSuccessfulBookmarkPost();
+ await visit("/t/internationalization-localization/280");
+ await openBookmarkModal();
+ await fillIn("input#bookmark-name", "Check this out later");
+ await click("#tap_tile_tomorrow");
+ await click("#save-bookmark");
+ assert.ok(
+ exists(".topic-post:first-child button.bookmark.bookmarked"),
+ "it shows the bookmarked icon on the post"
+ );
+ assert.ok(
+ exists(
+ ".topic-post:first-child button.bookmark.bookmarked > .d-icon-discourse-bookmark-clock"
+ ),
+ "it shows the bookmark clock icon because of the reminder"
+ );
+});
+
+test("Saving a bookmark with no reminder or name", async assert => {
+ mockSuccessfulBookmarkPost();
+ await visit("/t/internationalization-localization/280");
+ await openBookmarkModal();
+ await click("#save-bookmark");
+ assert.ok(
+ exists(".topic-post:first-child button.bookmark.bookmarked"),
+ "it shows the bookmarked icon on the post"
+ );
+ assert.not(
+ exists(
+ ".topic-post:first-child button.bookmark.bookmarked > .d-icon-discourse-bookmark-clock"
+ ),
+ "it shows the regular bookmark active icon"
+ );
+});
+
+test("Deleting a bookmark with a reminder", async assert => {
+ pretender.delete("/bookmarks/999", () => [
+ 200,
+ {
+ "Content-Type": "application/json"
+ },
+ {
+ success: "OK",
+ topic_bookmarked: false
+ }
+ ]);
+ mockSuccessfulBookmarkPost();
+ await visit("/t/internationalization-localization/280");
+ await openBookmarkModal();
+ await click("#tap_tile_tomorrow");
+ await click("#save-bookmark");
+ await openEditBookmarkModal();
+ assert.ok(exists("#bookmark-reminder-modal"), "it shows the bookmark modal");
+ await click("#delete-bookmark");
+ assert.ok(exists(".bootbox.modal"), "it asks for delete confirmation");
+ assert.ok(
+ find(".bootbox.modal")
+ .text()
+ .includes(I18n.t("bookmarks.confirm_delete")),
+ "it shows delete confirmation message"
+ );
+ await click(".bootbox.modal .btn-primary");
+ assert.not(
+ exists(".topic-post:first-child button.bookmark.bookmarked"),
+ "it no longer shows the bookmarked icon on the post after bookmark is deleted"
+ );
+});
+
+test("Cancelling saving a bookmark", async assert => {
+ await visit("/t/internationalization-localization/280");
+ await openBookmarkModal();
+ await click(".d-modal-cancel");
+ assert.not(
+ exists(".topic-post:first-child button.bookmark.bookmarked"),
+ "it does not show the bookmarked icon on the post because it is not saved"
+ );
+});
+
+test("Editing a bookmark", async assert => {
+ mockSuccessfulBookmarkPost();
+ await visit("/t/internationalization-localization/280");
+ let now = moment.tz(loggedInUser().resolvedTimezone());
+ let tomorrow = now.add(1, "day").format("YYYY-MM-DD");
+ await openBookmarkModal();
+ await fillIn("input#bookmark-name", "Test name");
+ await click("#tap_tile_tomorrow");
+ await click("#save-bookmark");
+ await openEditBookmarkModal();
+ assert.equal(
+ find("#bookmark-name").val(),
+ "Test name",
+ "it should prefill the bookmark name"
+ );
+ assert.equal(
+ find("#bookmark-custom-date > input").val(),
+ tomorrow,
+ "it should prefill the bookmark date"
+ );
+ assert.equal(
+ find("#bookmark-custom-time").val(),
+ "08:00",
+ "it should prefill the bookmark time"
+ );
+});
diff --git a/test/javascripts/acceptance/topic-test.js b/test/javascripts/acceptance/topic-test.js
index 94f3165dc96..ab271369c30 100644
--- a/test/javascripts/acceptance/topic-test.js
+++ b/test/javascripts/acceptance/topic-test.js
@@ -248,9 +248,9 @@ QUnit.test("remove featured link", async assert => {
"link to remove featured link"
);
- await click('.title-wrapper .remove-featured-link');
- await click('.title-wrapper .submit-edit');
- assert.ok(!exists('.title-wrapper .topic-featured-link'), 'link is gone');
+ await click(".title-wrapper .remove-featured-link");
+ await click(".title-wrapper .submit-edit");
+ assert.ok(!exists(".title-wrapper .topic-featured-link"), "link is gone");
});
QUnit.test("Converting to a public topic", async assert => {
@@ -395,17 +395,6 @@ QUnit.test(
}
);
-acceptance("Topic + Post Bookmarks with Reminders", {
- loggedIn: true
-});
-
-QUnit.test("Bookmarks Modal", async assert => {
- await visit("/t/internationalization-localization/280");
- await click(".topic-post:first-child button.show-more-actions");
- await click(".topic-post:first-child button.bookmark");
- assert.ok(exists("#bookmark-reminder-modal"), "it shows the bookmark modal");
-});
-
acceptance("Topic with title decorated", {
loggedIn: true,
beforeEach() {
diff --git a/test/javascripts/helpers/qunit-helpers.js b/test/javascripts/helpers/qunit-helpers.js
index e0621c294dd..0bc9eaece85 100644
--- a/test/javascripts/helpers/qunit-helpers.js
+++ b/test/javascripts/helpers/qunit-helpers.js
@@ -36,6 +36,11 @@ export function logIn() {
User.resetCurrent(currentUser());
}
+// Note: Only use if `loggedIn: true` has been used in an acceptance test
+export function loggedInUser() {
+ return User.current();
+}
+
const Plugin = $.fn.modal;
const Modal = Plugin.Constructor;