diff --git a/app/assets/javascripts/discourse-plugins/index.js b/app/assets/javascripts/discourse-plugins/index.js index df33693b319..4b5ed201fb6 100644 --- a/app/assets/javascripts/discourse-plugins/index.js +++ b/app/assets/javascripts/discourse-plugins/index.js @@ -113,18 +113,22 @@ module.exports = { directoryName, "test/javascripts" ); + const configDirectory = path.resolve(root, directoryName, "config"); const hasJs = fs.existsSync(jsDirectory); const hasAdminJs = fs.existsSync(adminJsDirectory); const hasTests = fs.existsSync(testDirectory); + const hasConfig = fs.existsSync(configDirectory); return { pluginName, directoryName, jsDirectory, adminJsDirectory, testDirectory, + configDirectory, hasJs, hasAdminJs, hasTests, + hasConfig, }; }); }, diff --git a/app/assets/javascripts/discourse/app/initializers/register-media-optimization-upload-processor.js b/app/assets/javascripts/discourse/app/initializers/register-media-optimization-upload-processor.js index 06027c8cf84..f80b68bde14 100644 --- a/app/assets/javascripts/discourse/app/initializers/register-media-optimization-upload-processor.js +++ b/app/assets/javascripts/discourse/app/initializers/register-media-optimization-upload-processor.js @@ -1,5 +1,6 @@ import { addComposerUploadPreProcessor } from "discourse/components/composer-editor"; import UppyMediaOptimization from "discourse/lib/uppy-media-optimization-plugin"; +import { Promise } from "rsvp"; export default { name: "register-media-optimization-upload-processor", @@ -11,10 +12,15 @@ export default { UppyMediaOptimization, ({ isMobileDevice }) => { return { - optimizeFn: (data, opts) => - container + optimizeFn: (data, opts) => { + if (container.isDestroyed || container.isDestroying) { + return Promise.resolve(); + } + + return container .lookup("service:media-optimization-worker") - .optimizeImage(data, opts), + .optimizeImage(data, opts); + }, runParallel: !isMobileDevice, }; } diff --git a/app/assets/javascripts/discourse/app/widgets/search-menu.js b/app/assets/javascripts/discourse/app/widgets/search-menu.js index c2c546af63b..56012ab3178 100644 --- a/app/assets/javascripts/discourse/app/widgets/search-menu.js +++ b/app/assets/javascripts/discourse/app/widgets/search-menu.js @@ -367,7 +367,7 @@ export default createWidget("search-menu", { return; } - if (e.which === 65 /* a */) { + if (e.key === "A") { if (document.activeElement?.classList.contains("search-link")) { if (document.querySelector("#reply-control.open")) { // add a link and focus composer @@ -388,8 +388,8 @@ export default createWidget("search-menu", { } } - const up = e.which === 38; - const down = e.which === 40; + const up = e.key === "ArrowUp"; + const down = e.key === "ArrowDown"; if (up || down) { let focused = document.activeElement.closest(".search-menu") ? document.activeElement @@ -443,7 +443,7 @@ export default createWidget("search-menu", { } const searchInput = document.querySelector("#search-term"); - if (e.which === 13 && e.target === searchInput) { + if (e.key === "Enter" && e.target === searchInput) { const recentEnterHit = this.state._lastEnterTimestamp && Date.now() - this.state._lastEnterTimestamp < SECOND_ENTER_MAX_DELAY; @@ -463,7 +463,7 @@ export default createWidget("search-menu", { this.state._lastEnterTimestamp = Date.now(); } - if (e.target === searchInput && e.which === 8 /* backspace */) { + if (e.target === searchInput && e.key === "Backspace") { if (!searchInput.value) { this.clearTopicContext(); this.clearPMInboxContext(); diff --git a/app/assets/javascripts/discourse/ember-cli-build.js b/app/assets/javascripts/discourse/ember-cli-build.js index 51e709b4c0e..dea7c1accef 100644 --- a/app/assets/javascripts/discourse/ember-cli-build.js +++ b/app/assets/javascripts/discourse/ember-cli-build.js @@ -6,6 +6,7 @@ const mergeTrees = require("broccoli-merge-trees"); const concat = require("broccoli-concat"); const prettyTextEngine = require("./lib/pretty-text-engine"); const { createI18nTree } = require("./lib/translation-plugin"); +const { parsePluginClientSettings } = require("./lib/site-settings-plugin"); const discourseScss = require("./lib/discourse-scss"); const generateScriptsTree = require("./lib/scripts"); const funnel = require("broccoli-funnel"); @@ -168,6 +169,7 @@ module.exports = function (defaults) { return mergeTrees([ createI18nTree(discourseRoot, vendorJs), + parsePluginClientSettings(discourseRoot, vendorJs, app), app.toTree(), funnel(`${discourseRoot}/public/javascripts`, { destDir: "javascripts" }), funnel(`${vendorJs}/highlightjs`, { diff --git a/app/assets/javascripts/discourse/lib/site-settings-plugin.js b/app/assets/javascripts/discourse/lib/site-settings-plugin.js new file mode 100644 index 00000000000..c576eee53d7 --- /dev/null +++ b/app/assets/javascripts/discourse/lib/site-settings-plugin.js @@ -0,0 +1,96 @@ +const Plugin = require("broccoli-plugin"); +const Yaml = require("js-yaml"); +const fs = require("fs"); +const concat = require("broccoli-concat"); +const mergeTrees = require("broccoli-merge-trees"); +const deepmerge = require("deepmerge"); +const glob = require("glob"); +const { shouldLoadPluginTestJs } = require("discourse/lib/plugin-js"); + +let built = false; + +class SiteSettingsPlugin extends Plugin { + constructor(inputNodes, inputFile, options) { + super(inputNodes, { + ...options, + persistentOutput: true, + }); + } + + build() { + if (built) { + return; + } + + let parsed = {}; + + this.inputPaths.forEach((path) => { + let inputFile; + if (path.includes("plugins")) { + inputFile = "settings.yml"; + } else { + inputFile = "site_settings.yml"; + } + const file = path + "/" + inputFile; + let yaml; + try { + yaml = fs.readFileSync(file, { encoding: "UTF-8" }); + } catch (err) { + // the plugin does not have a config file, go to the next file + return; + } + const loaded = Yaml.load(yaml, { json: true }); + parsed = deepmerge(parsed, loaded); + }); + + let clientSettings = {}; + // eslint-disable-next-line no-unused-vars + for (const [category, settings] of Object.entries(parsed)) { + for (const [setting, details] of Object.entries(settings)) { + if (details.client) { + clientSettings[setting] = details.default; + } + } + } + const contents = `var CLIENT_SITE_SETTINGS_WITH_DEFAULTS = ${JSON.stringify( + clientSettings + )}`; + + fs.writeFileSync(`${this.outputPath}/` + "settings_out.js", contents); + built = true; + } +} + +module.exports = function siteSettingsPlugin(...params) { + return new SiteSettingsPlugin(...params); +}; + +module.exports.parsePluginClientSettings = function ( + discourseRoot, + vendorJs, + app +) { + let settings = [discourseRoot + "/config"]; + + if (shouldLoadPluginTestJs()) { + const pluginInfos = app.project + .findAddonByName("discourse-plugins") + .pluginInfos(); + pluginInfos.forEach(({ hasConfig, configDirectory }) => { + if (hasConfig) { + settings = settings.concat(glob.sync(configDirectory)); + } + }); + } + + const loadedSettings = new SiteSettingsPlugin(settings, "site_settings.yml"); + + return concat(mergeTrees([loadedSettings]), { + inputFiles: [], + headerFiles: [], + footerFiles: [], + outputFile: `assets/test-site-settings.js`, + }); +}; + +module.exports.SiteSettingsPlugin = SiteSettingsPlugin; diff --git a/app/assets/javascripts/discourse/tests/acceptance/bootstrap-mode-notice-test.js b/app/assets/javascripts/discourse/tests/acceptance/bootstrap-mode-notice-test.js index 9d8812ccf3e..de3c793294d 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/bootstrap-mode-notice-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/bootstrap-mode-notice-test.js @@ -1,10 +1,9 @@ import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers"; import { test } from "qunit"; import { click, currentURL, settled, visit } from "@ember/test-helpers"; -import { set } from "@ember/object"; acceptance("Bootstrap Mode Notice", function (needs) { - needs.user(); + needs.user({ admin: true }); needs.site({ wizard_required: true }); needs.settings({ bootstrap_mode_enabled: true, @@ -36,8 +35,8 @@ acceptance("Bootstrap Mode Notice", function (needs) { "it transitions to the wizard page" ); + this.siteSettings.bootstrap_mode_enabled = false; await visit("/"); - set(this.siteSettings, "bootstrap_mode_enabled", false); await settled(); assert.ok( !exists(".bootstrap-mode-notice"), diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js index 2f1359d4d31..72a984ef666 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js @@ -24,7 +24,7 @@ acceptance("Composer Actions", function (needs) { }); needs.settings({ prioritize_username_in_ux: true, - display_name_on_post: false, + display_name_on_posts: false, enable_whispers: true, }); needs.site({ can_tag_topics: true }); @@ -489,7 +489,7 @@ acceptance("Prioritize Username", function (needs) { needs.user(); needs.settings({ prioritize_username_in_ux: true, - display_name_on_post: false, + display_name_on_posts: false, }); test("Reply to post use username", async function (assert) { @@ -517,7 +517,7 @@ acceptance("Prioritize Full Name", function (needs) { needs.user(); needs.settings({ prioritize_username_in_ux: false, - display_name_on_post: true, + display_name_on_posts: true, }); test("Reply to post use full name", async function (assert) { @@ -555,7 +555,7 @@ acceptance("Prioritizing Name fall back", function (needs) { needs.user(); needs.settings({ prioritize_username_in_ux: false, - display_name_on_post: true, + display_name_on_posts: true, }); test("Quotes fall back to username if name is not present", async function (assert) { diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-editor-mentions-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-editor-mentions-test.js index 6329a257690..29528b624a2 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-editor-mentions-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-editor-mentions-test.js @@ -18,7 +18,7 @@ acceptance("Composer - editor mentions", function (needs) { }; needs.user(); - needs.settings({ enable_mentions: true }); + needs.settings({ enable_mentions: true, allow_uncategorized_topics: true }); needs.hooks.afterEach(() => { if (clock) { diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-image-preview-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-image-preview-test.js index 4c730d62f5c..75103a3c9ba 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-image-preview-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-image-preview-test.js @@ -11,7 +11,7 @@ import { test } from "qunit"; acceptance("Composer - Image Preview", function (needs) { needs.user(); - needs.settings({ enable_whispers: true }); + needs.settings({ enable_whispers: true, allow_uncategorized_topics: true }); needs.site({ can_tag_topics: true }); needs.pretender((server, helper) => { server.post("/uploads/lookup-urls", () => { diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-tags-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-tags-test.js index fcea9970858..07a290a7d53 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-tags-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-tags-test.js @@ -18,6 +18,7 @@ acceptance("Composer - Tags", function (needs) { }); }); needs.site({ can_tag_topics: true }); + needs.settings({ allow_uncategorized_topics: true }); test("staff bypass tag validation rule", async function (assert) { await visit("/"); diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js index fe74e72aa76..a86afe5d080 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js @@ -302,7 +302,7 @@ acceptance("Composer", function (needs) { await visit("/"); await click("#create-topic"); await fillIn("#reply-title", "This title doesn't matter"); - await fillIn(".d-editor-input", "custom message"); + await fillIn(".d-editor-input", "custom message that is a good length"); await click("#reply-control button.create"); assert.strictEqual( @@ -1107,6 +1107,7 @@ acceptance("Composer - Customizations", function (needs) { acceptance("Composer - Focus Open and Closed", function (needs) { needs.user(); + needs.settings({ allow_uncategorized_topics: true }); test("Focusing a composer which is not open with create topic", async function (assert) { await visit("/t/internationalization-localization/280"); diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-uploads-uppy-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-uploads-uppy-test.js index 2f62ab4cad8..8e4a3b5c82c 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/composer-uploads-uppy-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/composer-uploads-uppy-test.js @@ -81,6 +81,7 @@ acceptance("Uppy Composer Attachment - Upload Placeholder", function (needs) { needs.settings({ simultaneous_uploads: 2, enable_rich_text_paste: true, + allow_uncategorized_topics: true, }); needs.hooks.afterEach(() => { uploadNumber = 1; @@ -487,6 +488,7 @@ acceptance("Uppy Composer Attachment - Upload Error", function (needs) { }); needs.settings({ simultaneous_uploads: 2, + allow_uncategorized_topics: true, }); test("should show an error message for the failed upload", async function (assert) { @@ -527,6 +529,7 @@ acceptance("Uppy Composer Attachment - Upload Handler", function (needs) { needs.pretender(pretender); needs.settings({ simultaneous_uploads: 2, + allow_uncategorized_topics: true, }); needs.hooks.beforeEach(() => { withPluginApi("0.8.14", (api) => { diff --git a/app/assets/javascripts/discourse/tests/acceptance/emoji-test.js b/app/assets/javascripts/discourse/tests/acceptance/emoji-test.js index 9aabd553eaf..1b4aeb8e752 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/emoji-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/emoji-test.js @@ -21,7 +21,7 @@ acceptance("Emoji", function (needs) { assert.strictEqual( normalizeHtml(query(".d-editor-preview").innerHTML.trim()), normalizeHtml( - `
this is an emoji
` + `this is an emoji
` ) ); }); @@ -36,7 +36,7 @@ acceptance("Emoji", function (needs) { assert.strictEqual( normalizeHtml(query(".d-editor-preview").innerHTML.trim()), normalizeHtml( - `this is an emoji
` + `this is an emoji
` ) ); }); diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js index 502be77b6d4..abbde3f6038 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js @@ -94,7 +94,7 @@ acceptance("Group Requests", function (needs) { query(".group-members tr:first-child td:nth-child(1)") .innerText.trim() .replace(/\s+/g, " "), - "Robin Ward eviltrout" + "eviltrout Robin Ward" ); assert.strictEqual( query(".group-members tr:first-child td:nth-child(3)").innerText.trim(), diff --git a/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js b/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js index 982f854be30..e3f03607608 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js @@ -123,7 +123,7 @@ acceptance("Invite accept", function (needs) { "submit is disabled because password is not filled" ); - await fillIn("#new-account-password", "top$ecret"); + await fillIn("#new-account-password", "top$ecretzz"); assert.notOk( exists(".invites-show .btn-primary:disabled"), "submit is enabled" diff --git a/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js b/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js index 27f91bf1e35..38891cc8e94 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js @@ -128,6 +128,9 @@ acceptance("User Preferences", function (needs) { await categorySelector.fillInFilter("faq"); await savePreferences(); + this.siteSettings.tagging_enabled = false; + await visit("/"); + await visit("/u/eviltrout/preferences"); assert.ok( !exists(".preferences-nav .nav-tags a"), "tags tab isn't there when tags are disabled" diff --git a/app/assets/javascripts/discourse/tests/acceptance/search-test.js b/app/assets/javascripts/discourse/tests/acceptance/search-test.js index 751623da217..321d6cf107e 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/search-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/search-test.js @@ -4,7 +4,13 @@ import { exists, query, } from "discourse/tests/helpers/qunit-helpers"; -import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers"; +import { + click, + fillIn, + settled, + triggerKeyEvent, + visit, +} from "@ember/test-helpers"; import I18n from "I18n"; import searchFixtures from "discourse/tests/fixtures/search-fixtures"; import selectKit from "discourse/tests/helpers/select-kit-helper"; @@ -333,7 +339,10 @@ acceptance("Search - Anonymous", function (needs) { acceptance("Search - Authenticated", function (needs) { needs.user(); - needs.settings({ log_search_queries: true }); + needs.settings({ + log_search_queries: true, + allow_uncategorized_topics: true, + }); needs.pretender((server, helper) => { server.get("/search/query", (request) => { @@ -476,6 +485,7 @@ acceptance("Search - Authenticated", function (needs) { "href" ); await triggerKeyEvent(".search-menu", "keydown", "A"); + await settled(); assert.strictEqual( query("#reply-control textarea").value, diff --git a/app/assets/javascripts/discourse/tests/acceptance/sidebar-user-categories-section-test.js b/app/assets/javascripts/discourse/tests/acceptance/sidebar-user-categories-section-test.js index f7fcd4c6d70..3b4a14cd4cb 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/sidebar-user-categories-section-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/sidebar-user-categories-section-test.js @@ -69,6 +69,7 @@ acceptance("Sidebar - Logged on user - Categories Section", function (needs) { enable_experimental_sidebar_hamburger: true, enable_sidebar: true, suppress_uncategorized_badge: false, + allow_uncategorized_topics: true, }); needs.pretender((server, helper) => { diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-discovery-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-discovery-test.js index 5106f49861d..5e0a1ed5e49 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/topic-discovery-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/topic-discovery-test.js @@ -30,7 +30,7 @@ acceptance("Topic Discovery", function (needs) { assert.strictEqual( query("a[data-user-card=eviltrout] img.avatar").getAttribute("title"), - "Evil Trout - Most Posts", + "eviltrout - Most Posts", "it shows user's full name in avatar title" ); diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-edit-timer-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-edit-timer-test.js index ad6f4a6082d..39d77bdd8b4 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/topic-edit-timer-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/topic-edit-timer-test.js @@ -134,7 +134,7 @@ acceptance("Topic - Edit timer", function (needs) { await timerType.expand(); await timerType.selectRowByValue("publish_to_category"); - assert.strictEqual(categoryChooser.header().label(), "uncategorized"); + assert.strictEqual(categoryChooser.header().label(), "category…"); assert.strictEqual(categoryChooser.header().value(), null); await categoryChooser.expand(); @@ -174,7 +174,7 @@ acceptance("Topic - Edit timer", function (needs) { await timerType.expand(); await timerType.selectRowByValue("publish_to_category"); - assert.strictEqual(categoryChooser.header().label(), "uncategorized"); + assert.strictEqual(categoryChooser.header().label(), "category…"); assert.strictEqual(categoryChooser.header().value(), null); await categoryChooser.expand(); @@ -218,7 +218,7 @@ acceptance("Topic - Edit timer", function (needs) { await timerType.expand(); await timerType.selectRowByValue("publish_to_category"); - assert.strictEqual(categoryChooser.header().label(), "uncategorized"); + assert.strictEqual(categoryChooser.header().label(), "category…"); assert.strictEqual(categoryChooser.header().value(), null); await categoryChooser.expand(); diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js index 2859e2c9110..241d99d1a32 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js @@ -20,6 +20,19 @@ acceptance("Topic - Quote button - logged in", function (needs) { share_quote_buttons: "twitter|email", }); + needs.pretender((server, helper) => { + server.get("/inline-onebox", () => + helper.response({ + "inline-oneboxes": [ + { + url: "http://www.example.com/57350945", + title: "This is a great title", + }, + ], + }) + ); + }); + chromeTest( "Does not show the quote share buttons by default", async function (assert) { diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-test.js index 2f91b78a6b3..45a40f095d0 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/topic-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/topic-test.js @@ -211,6 +211,7 @@ acceptance("Topic", function (needs) { }); test("Deleting a topic", async function (assert) { + this.siteSettings.min_topic_views_for_delete_confirm = 10000; await visit("/t/internationalization-localization/280"); await click(".topic-post:nth-of-type(1) button.show-more-actions"); await click(".widget-button.delete"); diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js index fef99da02c5..36422c742c0 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js @@ -58,7 +58,7 @@ acceptance("User Drafts", function (needs) { query(".user-stream-item:nth-child(3) .excerpt").innerHTML.trim() ), normalizeHtml( - `here goes a reply to a PM ` + `here goes a reply to a PM ` ), "shows the excerpt" ); diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-test.js index e54642d5fee..c84da617f8d 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/user-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/user-test.js @@ -120,14 +120,16 @@ acceptance( await visit("/u/eviltrout"); assert.strictEqual( query(".user-profile-names .username").textContent.trim(), - "eviltrout", + `eviltrout + Robin Ward is an admin`, "eviltrout profile is shown" ); await visit("/u/e.il.rout"); assert.strictEqual( query(".user-profile-names .username").textContent.trim(), - "e.il.rout", + `e.il.rout + Robin Ward is an admin`, "e.il.rout profile is shown" ); }); diff --git a/app/assets/javascripts/discourse/tests/helpers/create-pretender.js b/app/assets/javascripts/discourse/tests/helpers/create-pretender.js index 0cf90293f92..c406d940657 100644 --- a/app/assets/javascripts/discourse/tests/helpers/create-pretender.js +++ b/app/assets/javascripts/discourse/tests/helpers/create-pretender.js @@ -612,7 +612,22 @@ export function applyDefaultHandlers(pretender) { pretender.post("/posts", function (request) { const data = parsePostData(request.requestBody); - if (data.raw === "custom message") { + if (data.title === "this title triggers an error") { + return response(422, { errors: ["That title has already been taken"] }); + } + + if (data.raw === "enqueue this content please") { + return response(200, { + success: true, + action: "enqueued", + pending_post: { + id: 1234, + raw: data.raw, + }, + }); + } + + if (data.raw === "custom message that is a good length") { return response(200, { success: true, action: "custom", diff --git a/app/assets/javascripts/discourse/tests/helpers/site-settings.js b/app/assets/javascripts/discourse/tests/helpers/site-settings.js index f822ec1365b..9f64e064ca0 100644 --- a/app/assets/javascripts/discourse/tests/helpers/site-settings.js +++ b/app/assets/javascripts/discourse/tests/helpers/site-settings.js @@ -1,111 +1,27 @@ -const ORIGINAL_SETTINGS = { +const CLIENT_SETTING_TEST_OVERRIDES = { title: "QUnit Discourse Tests", site_logo_url: "/assets/logo.png", site_logo_url: "/assets/logo.png", site_logo_small_url: "/assets/logo-single.png", site_mobile_logo_url: "", site_favicon_url: "/images/discourse-logo-sketch-small.png", - allow_user_locale: false, - suggested_topics: 7, - ga_universal_tracking_code: "", - ga_universal_domain_name: "auto", - top_menu: "latest|new|unread|categories|top", - post_menu: "like|share|flag|edit|bookmark|delete|admin|reply", - post_menu_hidden_items: "flag|bookmark|edit|delete|admin", - share_links: "twitter|facebook|email", - allow_username_in_share_links: true, - category_colors: - "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|27AA5B|B3B5B4|E45735", - enable_mobile_theme: true, - relative_date_duration: 14, - fixed_category_positions: false, - enable_badges: true, - invite_only: false, - login_required: false, - must_approve_users: false, - enable_local_logins: true, - allow_new_registrations: true, - enable_google_logins: true, - enable_google_oauth2_logins: false, enable_twitter_logins: true, enable_facebook_logins: true, enable_github_logins: true, - enable_discourse_connect: false, - min_username_length: 3, - max_username_length: 20, - min_password_length: 8, - enable_names: true, - invites_shown: 30, - delete_user_max_post_age: 60, - delete_all_posts_max: 15, - min_post_length: 20, - min_personal_message_post_length: 10, - max_post_length: 32000, - min_topic_title_length: 15, - max_topic_title_length: 255, - min_personal_message_title_length: 2, - allow_uncategorized_topics: true, - min_title_similar_length: 10, - edit_history_visible_to_public: true, - delete_removed_posts_after: 24, - traditional_markdown_linebreaks: false, - suppress_reply_directly_below: true, - suppress_reply_directly_above: true, - newuser_max_embedded_media: 0, - newuser_max_attachments: 0, - display_name_on_posts: true, - short_progress_text_threshold: 10000, - default_code_lang: "auto", - autohighlight_all_code: false, - email_in: false, - authorized_extensions: ".jpg|.jpeg|.png|.gif|.svg|.txt|.ico|.yml", - authorized_extensions_for_staff: "", - max_image_width: 690, - max_image_height: 500, - allow_profile_backgrounds: true, - allow_uploaded_avatars: "0", - tl1_requires_read_posts: 30, - polling_interval: 3000, + authorized_extensions: "jpg|jpeg|png|gif|heic|heif|webp|svg|txt|ico|yml", anon_polling_interval: 30000, flush_timings_secs: 5, - enable_user_directory: true, - tos_url: "", - privacy_policy_url: "", - tos_accept_required: false, - faq_url: "", - allow_restore: false, - maximum_backups: 5, - version_checks: true, - suppress_uncategorized_badge: true, - min_search_term_length: 3, - topic_views_heat_low: 1000, - topic_views_heat_medium: 2000, - topic_views_heat_high: 5000, - global_notice: "", - show_create_topics_notice: true, - available_locales: - "cs|da|de|en|es|fr|he|id|it|ja|ko|nb_NO|nl|pl_PL|pt|pt_BR|ru|sv|uk|zh_CN|zh_TW", - highlighted_languages: - "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", - enable_emoji: true, - enable_emoji_shortcuts: true, - emoji_set: "google_classic", - enable_emoji_shortcuts: true, - enable_inline_emoji_translation: false, - desktop_category_page_style: "categories_and_latest_topics", - enable_mentions: true, - enable_personal_messages: true, - personal_message_enabled_groups: "11", // TL1 group - unicode_usernames: false, - secure_uploads: false, - external_emoji_url: "", - remove_muted_tags_from_latest: "always", - enable_group_directory: true, - default_sidebar_categories: "", - default_sidebar_tags: "", }; -let siteSettings = Object.assign({}, ORIGINAL_SETTINGS); +// Note, CLIENT_SITE_SETTINGS_WITH_DEFAULTS is generated by the site-settings-plugin, +// writing to test-site-settings.js via the ember-cli-build pipeline. +const ORIGINAL_CLIENT_SITE_SETTINGS = Object.assign( + {}, + // eslint-disable-next-line no-undef + CLIENT_SITE_SETTINGS_WITH_DEFAULTS, + CLIENT_SETTING_TEST_OVERRIDES +); +let siteSettings = Object.assign({}, ORIGINAL_CLIENT_SITE_SETTINGS); export function currentSettings() { return siteSettings; @@ -135,7 +51,8 @@ export function mergeSettings(other) { export function resetSettings() { for (let p in siteSettings) { if (siteSettings.hasOwnProperty(p)) { - let v = ORIGINAL_SETTINGS[p]; + // eslint-disable-next-line no-undef + let v = ORIGINAL_CLIENT_SITE_SETTINGS[p]; typeof v !== "undefined" ? setValue(p, v) : delete siteSettings[p]; } } diff --git a/app/assets/javascripts/discourse/tests/index.html b/app/assets/javascripts/discourse/tests/index.html index c286633609f..d1276bb7c8f 100644 --- a/app/assets/javascripts/discourse/tests/index.html +++ b/app/assets/javascripts/discourse/tests/index.html @@ -37,6 +37,7 @@ + {{content-for "body"}} {{content-for "test-body"}} diff --git a/app/assets/javascripts/discourse/tests/integration/components/widgets/hamburger-menu-test.js b/app/assets/javascripts/discourse/tests/integration/components/widgets/hamburger-menu-test.js index 23b4a074dbe..cb9ccb3bf31 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/widgets/hamburger-menu-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/widgets/hamburger-menu-test.js @@ -96,6 +96,7 @@ module("Integration | Component | Widget | hamburger-menu", function (hooks) { [...queryAll(".category-link .category-name")].map((el) => el.innerText), this.site .get("categoriesByCount") + .reject((c) => c.id === this.site.uncategorized_category_id) .slice(0, 8) .map((c) => c.name) ); @@ -103,7 +104,7 @@ module("Integration | Component | Widget | hamburger-menu", function (hooks) { test("top categories - allow_uncategorized_topics", async function (assert) { this.owner.unregister("service:current-user"); - this.siteSettings.allow_uncategorized_topics = false; + this.siteSettings.allow_uncategorized_topics = true; this.siteSettings.header_dropdown_category_count = 8; await render(hbs`@sam
`, + `@sam
`, "cooks emojis when only the emoji markdown engine is enabled" ); @@ -1539,15 +1539,15 @@ var bar = 'bar'; test("emoji", function (assert) { assert.cooked( ":smile:", - `` + `` ); assert.cooked( ":(", - `` + `` ); assert.cooked( "8-)", - `` + `` ); }); @@ -1561,7 +1561,7 @@ var bar = 'bar'; assert.cookedOptions( "test:smile:test", { siteSettings: { enable_inline_emoji_translation: true } }, - `testtest
` + `testtest
` ); }); diff --git a/app/assets/javascripts/discourse/tests/unit/models/composer-test.js b/app/assets/javascripts/discourse/tests/unit/models/composer-test.js index eb3f513dd25..fb7f80420f0 100644 --- a/app/assets/javascripts/discourse/tests/unit/models/composer-test.js +++ b/app/assets/javascripts/discourse/tests/unit/models/composer-test.js @@ -382,6 +382,7 @@ discourseModule("Unit | Model | composer", function () { }); test("title placeholder depends on what you're doing", function (assert) { + this.siteSettings.topic_featured_link_enabled = false; let composer = createComposer({ action: CREATE_TOPIC }); assert.strictEqual( composer.get("titlePlaceholder"), diff --git a/app/assets/javascripts/discourse/tests/unit/models/topic-test.js b/app/assets/javascripts/discourse/tests/unit/models/topic-test.js index 7483d833252..c4dc505d923 100644 --- a/app/assets/javascripts/discourse/tests/unit/models/topic-test.js +++ b/app/assets/javascripts/discourse/tests/unit/models/topic-test.js @@ -204,7 +204,7 @@ discourseModule("Unit | Model | topic", function (hooks) { assert.strictEqual( topic.get("fancyTitle"), - ` with all the emojis `, + ` with all the emojis `, "supports emojis" ); }); @@ -238,7 +238,7 @@ discourseModule("Unit | Model | topic", function (hooks) { assert.strictEqual( topic.get("escapedExcerpt"), - `This is a test topic `, + `This is a test topic `, "supports emojis" ); }); diff --git a/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js b/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js index 8ce6442575b..e0813c2310d 100644 --- a/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js +++ b/app/assets/javascripts/pretty-text/addon/engines/discourse-markdown-it.js @@ -385,6 +385,12 @@ function setupMarkdownEngine(opts, featureConfig) { opts.pluginCallbacks.forEach(([feature, callback]) => { if (featureConfig[feature]) { + if (callback === null || callback === undefined) { + // eslint-disable-next-line no-console + console.log("BAD MARKDOWN CALLBACK FOUND"); + // eslint-disable-next-line no-console + console.log(`FEATURE IS: ${feature}`); + } opts.engine.use(callback); } }); diff --git a/plugins/chat/test/javascripts/acceptance/chat-composer-test.js b/plugins/chat/test/javascripts/acceptance/chat-composer-test.js index 7963f3a2b44..cb603048309 100644 --- a/plugins/chat/test/javascripts/acceptance/chat-composer-test.js +++ b/plugins/chat/test/javascripts/acceptance/chat-composer-test.js @@ -67,7 +67,10 @@ acceptance("Discourse Chat - Composer", function (needs) { "service:chat-emoji-reaction-store" ); - assert.deepEqual(emojiReactionStore.favorites, []); + assert.deepEqual( + emojiReactionStore.favorites, + this.siteSettings.default_emoji_reactions.split("|") + ); await visit("/chat/channel/11/-"); await click(".chat-composer-dropdown__trigger-btn"); @@ -76,7 +79,7 @@ acceptance("Discourse Chat - Composer", function (needs) { assert.deepEqual( emojiReactionStore.favorites, - ["grinning"], + ["grinning"].concat(this.siteSettings.default_emoji_reactions.split("|")), "it tracks the emoji" ); }); @@ -86,7 +89,10 @@ acceptance("Discourse Chat - Composer", function (needs) { "service:chat-emoji-reaction-store" ); - assert.deepEqual(emojiReactionStore.favorites, []); + assert.deepEqual( + emojiReactionStore.favorites, + this.siteSettings.default_emoji_reactions.split("|") + ); await visit("/chat/channel/11/-"); await fillIn(".chat-composer-input", "test :grinni"); @@ -95,7 +101,7 @@ acceptance("Discourse Chat - Composer", function (needs) { assert.deepEqual( emojiReactionStore.favorites, - ["grinning"], + ["grinning"].concat(this.siteSettings.default_emoji_reactions.split("|")), "it tracks the emoji" ); }); diff --git a/plugins/chat/test/javascripts/acceptance/chat-message-test.js b/plugins/chat/test/javascripts/acceptance/chat-message-test.js index 8bb00f7ebf4..5ddc50ecdde 100644 --- a/plugins/chat/test/javascripts/acceptance/chat-message-test.js +++ b/plugins/chat/test/javascripts/acceptance/chat-message-test.js @@ -30,7 +30,10 @@ acceptance("Discourse Chat - Chat Message", function (needs) { "service:chat-emoji-reaction-store" ); - assert.deepEqual(emojiReactionStore.favorites, []); + assert.deepEqual( + emojiReactionStore.favorites, + this.siteSettings.default_emoji_reactions.split("|") + ); await visit("/chat/channel/4/public-category"); await click( @@ -39,7 +42,11 @@ acceptance("Discourse Chat - Chat Message", function (needs) { assert.deepEqual( emojiReactionStore.favorites, - ["heart"], + ["heart"].concat( + this.siteSettings.default_emoji_reactions + .split("|") + .filter((r) => r !== "heart") + ), "it tracks the emoji" ); @@ -49,7 +56,11 @@ acceptance("Discourse Chat - Chat Message", function (needs) { assert.deepEqual( emojiReactionStore.favorites, - ["heart"], + ["heart"].concat( + this.siteSettings.default_emoji_reactions + .split("|") + .filter((r) => r !== "heart") + ), "it doesn’t untrack when removing the reaction" ); }); @@ -59,7 +70,10 @@ acceptance("Discourse Chat - Chat Message", function (needs) { "service:chat-emoji-reaction-store" ); - assert.deepEqual(emojiReactionStore.favorites, []); + assert.deepEqual( + emojiReactionStore.favorites, + this.siteSettings.default_emoji_reactions.split("|") + ); await visit("/chat/channel/4/public-category"); await triggerEvent(".chat-message-container[data-id='176']", "mouseenter"); @@ -68,7 +82,7 @@ acceptance("Discourse Chat - Chat Message", function (needs) { assert.deepEqual( emojiReactionStore.favorites, - ["grinning"], + ["grinning"].concat(this.siteSettings.default_emoji_reactions.split("|")), "it tracks the emoji" ); @@ -78,7 +92,7 @@ acceptance("Discourse Chat - Chat Message", function (needs) { assert.deepEqual( emojiReactionStore.favorites, - ["grinning"], + ["grinning"].concat(this.siteSettings.default_emoji_reactions.split("|")), "it doesn’t untrack when removing the reaction" ); }); diff --git a/plugins/chat/test/javascripts/unit/lib/chat-emoji-reaction-store-test.js b/plugins/chat/test/javascripts/unit/lib/chat-emoji-reaction-store-test.js index 5d29b55a789..b53a2084dc5 100644 --- a/plugins/chat/test/javascripts/unit/lib/chat-emoji-reaction-store-test.js +++ b/plugins/chat/test/javascripts/unit/lib/chat-emoji-reaction-store-test.js @@ -85,7 +85,10 @@ module("Discourse Chat | Unit | chat-emoji-reaction-store", function (hooks) { this.chatEmojiReactionStore.storedFavorites = []; this.chatEmojiReactionStore.track("yum"); - assert.deepEqual(this.chatEmojiReactionStore.storedFavorites, ["yum"]); + assert.deepEqual( + this.chatEmojiReactionStore.storedFavorites, + ["yum"].concat(this.siteSettings.default_emoji_reactions.split("|")) + ); }); test("#storedFavorites when tracking different emojis", function (assert) { @@ -97,7 +100,9 @@ module("Discourse Chat | Unit | chat-emoji-reaction-store", function (hooks) { assert.deepEqual( this.chatEmojiReactionStore.storedFavorites, - ["grinning", "yum", "not_yum", "yum"], + ["grinning", "yum", "not_yum", "yum"].concat( + this.siteSettings.default_emoji_reactions.split("|") + ), "it ensures last in is first" ); }); diff --git a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js index a0897f6c422..8a63df55175 100644 --- a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js +++ b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js @@ -14,6 +14,9 @@ acceptance("Details Button", function (needs) { await visit("/"); await click("#create-topic"); + const categoryChooser = selectKit(".category-chooser"); + await categoryChooser.expand(); + await categoryChooser.selectRowByValue(2); await popupMenu.expand(); await popupMenu.selectRowByValue("insertDetails"); @@ -115,6 +118,9 @@ acceptance("Details Button", function (needs) { await visit("/"); await click("#create-topic"); + const categoryChooser = selectKit(".category-chooser"); + await categoryChooser.expand(); + await categoryChooser.selectRowByValue(2); await fillIn(".d-editor-input", multilineInput); const textarea = query(".d-editor-input"); diff --git a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js index a436ce76df2..b7019a8ba5c 100644 --- a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js +++ b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js @@ -4,7 +4,7 @@ import { module, test } from "qunit"; const defaultOpts = buildOptions({ siteSettings: { enable_emoji: true, - emoji_set: "google_classic", + emoji_set: "twitter", highlighted_languages: "json|ruby|javascript", default_code_lang: "auto", }, diff --git a/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js b/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js index 3c4855cb610..b0411398b89 100644 --- a/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js +++ b/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js @@ -23,6 +23,9 @@ acceptance("Local Dates - composer", function (needs) { await visit("/"); await click("#create-topic"); + const categoryChooser = selectKit(".category-chooser"); + await categoryChooser.expand(); + await categoryChooser.selectRowByValue(2); await fillIn( ".d-editor-input", @@ -73,6 +76,9 @@ acceptance("Local Dates - composer", function (needs) { test("date modal", async function (assert) { await visit("/"); await click("#create-topic"); + const categoryChooser = selectKit(".category-chooser"); + await categoryChooser.expand(); + await categoryChooser.selectRowByValue(2); await click(".d-editor-button-bar .local-dates"); const timezoneChooser = selectKit(".timezone-input"); @@ -88,6 +94,9 @@ acceptance("Local Dates - composer", function (needs) { test("date modal - controls", async function (assert) { await visit("/"); await click("#create-topic"); + const categoryChooser = selectKit(".category-chooser"); + await categoryChooser.expand(); + await categoryChooser.selectRowByValue(2); await click(".d-editor-button-bar .local-dates"); await click('.pika-table td[data-day="5"] > .pika-button'); diff --git a/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-quoting-test.js b/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-quoting-test.js index 265cf349401..5c170340188 100644 --- a/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-quoting-test.js +++ b/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-quoting-test.js @@ -49,7 +49,7 @@ acceptance("Local Dates - quoting", function (needs) { await click(".insert-quote"); assert.strictEqual( query(".d-editor-input").value.trim(), - `[quote=\"Uwe Keim, post:1, topic:280, username:uwe_keim\"] + `[quote=\"uwe_keim, post:1, topic:280\"] This is a test [date=2022-06-17 time=10:00:00 timezone="Australia/Brisbane" displayedTimezone="Australia/Perth"] [/quote]`, "converts the date to markdown with all options correctly" @@ -88,7 +88,7 @@ acceptance("Local Dates - quoting range", function (needs) { await click(".insert-quote"); assert.strictEqual( query(".d-editor-input").value.trim(), - `[quote=\"Uwe Keim, post:1, topic:280, username:uwe_keim\"] + `[quote=\"uwe_keim, post:1, topic:280\"] Some text [date-range from=2022-06-17T09:30:00 to=2022-06-18T10:30:00 format="LL" timezone="Australia/Brisbane" timezones="Africa/Accra|Australia/Brisbane|Europe/Paris"] [/quote]`, "converts the date range to markdown with all options correctly" @@ -130,7 +130,7 @@ acceptance( await click(".insert-quote"); assert.strictEqual( query(".d-editor-input").value.trim(), - `[quote=\"Uwe Keim, post:1, topic:280, username:uwe_keim\"] + `[quote=\"uwe_keim, post:1, topic:280\"] Testing countdown [date=2022-06-21 time=09:30:00 format="LL" timezone="Australia/Brisbane" countdown="true"] Testing recurring [date=2022-06-22 timezone="Australia/Brisbane" recurring="2.weeks"] diff --git a/plugins/discourse-presence/test/javascripts/acceptance/discourse-presence-test.js b/plugins/discourse-presence/test/javascripts/acceptance/discourse-presence-test.js index 72c9d9e18f0..9487e88c96d 100644 --- a/plugins/discourse-presence/test/javascripts/acceptance/discourse-presence-test.js +++ b/plugins/discourse-presence/test/javascripts/acceptance/discourse-presence-test.js @@ -21,6 +21,9 @@ acceptance("Discourse Presence Plugin", function (needs) { test("Doesn't break topic creation", async function (assert) { await visit("/"); await click("#create-topic"); + const categoryChooser = selectKit(".category-chooser"); + await categoryChooser.expand(); + await categoryChooser.selectRowByValue(2); await fillIn("#reply-title", "Internationalization Localization"); await fillIn( ".d-editor-input",