From 1157ff8116ba5e5d11db589e1b6cb930d2c86c4d Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 19 Nov 2020 12:29:55 -0500 Subject: [PATCH 01/71] FIX: There are two elements with a `suspend-reason` class We only want to fill in the text one. (This is an Ember CLI fix) --- .../discourse/tests/acceptance/admin-suspend-user-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js index a18b723e90..af8f6d5834 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js @@ -42,7 +42,7 @@ acceptance("Admin - Suspend User", function (needs) { assert.equal(queryAll(".suspend-user-modal:visible").length, 1); - await fillIn(".suspend-reason", "for breaking the rules"); + await fillIn("input.suspend-reason", "for breaking the rules"); await fillIn(".suspend-message", "this is an email reason why"); await click(".d-modal-cancel"); From c244214fe5e2840bd71f284500b0d49ffc472d4a Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Fri, 20 Nov 2020 09:09:18 +0530 Subject: [PATCH 02/71] UX: update "Rebuild-HTML" icon (#11296) --- app/assets/javascripts/discourse/app/widgets/post-admin-menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js b/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js index b2c509fb57..bc1b4369c5 100644 --- a/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js +++ b/app/assets/javascripts/discourse/app/widgets/post-admin-menu.js @@ -141,7 +141,7 @@ export function buildManageButtons(attrs, currentUser, siteSettings) { if (attrs.canManage) { contents.push({ - icon: "cog", + icon: "sync-alt", label: "post.controls.rebake", action: "rebakePost", className: "popup-menu-button rebuild-html", From 9b7525bb03a2965367f32d2aa786eccef6b70606 Mon Sep 17 00:00:00 2001 From: Dan Ungureanu Date: Fri, 20 Nov 2020 11:28:14 +0200 Subject: [PATCH 03/71] FIX: Handle uncaught exception (#11263) After the search term is parsed for advanced search filters, the term may become empty. Later, the same term will be passed to Discourse.route_for which will raise an ArgumentError. > URI(nil) ArgumentError: bad argument (expected URI object or URI string) --- lib/discourse.rb | 2 +- lib/search.rb | 11 ++++------- spec/requests/search_controller_spec.rb | 5 +++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/discourse.rb b/lib/discourse.rb index 636208587c..948cf9ac8d 100644 --- a/lib/discourse.rb +++ b/lib/discourse.rb @@ -412,7 +412,7 @@ module Discourse unless uri.is_a?(URI) uri = begin URI(uri) - rescue URI::Error + rescue ArgumentError, URI::Error end end diff --git a/lib/search.rb b/lib/search.rb index 7e7e5cfce4..c3f083b00f 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -266,14 +266,11 @@ class Search if @term =~ /^\d+$/ single_topic(@term.to_i) else - begin - if route = Discourse.route_for(@term) - if route[:controller] == "topics" && route[:action] == "show" - topic_id = (route[:id] || route[:topic_id]).to_i - single_topic(topic_id) if topic_id > 0 - end + if route = Discourse.route_for(@term) + if route[:controller] == "topics" && route[:action] == "show" + topic_id = (route[:id] || route[:topic_id]).to_i + single_topic(topic_id) if topic_id > 0 end - rescue ActionController::RoutingError end end end diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb index 5d7181f7a8..990bc15d97 100644 --- a/spec/requests/search_controller_spec.rb +++ b/spec/requests/search_controller_spec.rb @@ -231,6 +231,11 @@ describe SearchController do expect(SearchLog.where(term: 'wookie')).to be_blank end + it "does not raise 500 with an empty term" do + get "/search/query.json", params: { term: "in:first", type_filter: "topic", search_for_id: true } + expect(response.status).to eq(200) + end + context 'rate limited' do it 'rate limits anon searches per user' do SiteSetting.rate_limit_search_anon_user = 2 From 96ceea8ec21ef690cfd0c7db1e603a31b2c2fedb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Nov 2020 21:01:50 +0000 Subject: [PATCH 04/71] Build(deps): Bump redis from 4.2.2 to 4.2.4 Bumps [redis](https://github.com/redis/redis-rb) from 4.2.2 to 4.2.4. - [Release notes](https://github.com/redis/redis-rb/releases) - [Changelog](https://github.com/redis/redis-rb/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/redis-rb/compare/v4.2.2...v4.2.4) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 946b7245a1..d4b3fba5a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -298,7 +298,7 @@ GEM msgpack (>= 0.4.3) optimist (>= 3.0.0) rchardet (1.8.0) - redis (4.2.2) + redis (4.2.4) redis-namespace (1.8.0) redis (>= 3.0.4) regexp_parser (1.8.2) From 8b83373becf5688a48b9dbb650fe6d8f2fb3d858 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Nov 2020 14:33:18 +0000 Subject: [PATCH 05/71] Build(deps): Bump redis from 4.2.4 to 4.2.5 (#11301) Bumps [redis](https://github.com/redis/redis-rb) from 4.2.4 to 4.2.5. - [Release notes](https://github.com/redis/redis-rb/releases) - [Changelog](https://github.com/redis/redis-rb/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/redis-rb/compare/v4.2.4...v4.2.5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d4b3fba5a1..5c55ec31b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -298,7 +298,7 @@ GEM msgpack (>= 0.4.3) optimist (>= 3.0.0) rchardet (1.8.0) - redis (4.2.4) + redis (4.2.5) redis-namespace (1.8.0) redis (>= 3.0.4) regexp_parser (1.8.2) From ca9abb4f9007108868930544b9aba642f6f9c507 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Nov 2020 14:35:36 +0000 Subject: [PATCH 06/71] Build(deps): Bump rails_failover from 0.6.0 to 0.6.1 (#11302) Bumps rails_failover from 0.6.0 to 0.6.1. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5c55ec31b6..d01a59fb51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -275,7 +275,7 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - rails_failover (0.6.0) + rails_failover (0.6.1) activerecord (~> 6.0) railties (~> 6.0) rails_multisite (2.5.0) From dbcf722ab902b2c811b429d5969d3e7848634048 Mon Sep 17 00:00:00 2001 From: Jarek Radosz Date: Fri, 20 Nov 2020 15:54:09 +0100 Subject: [PATCH 07/71] DEV: Modulize component tests (#11300) It's like the new tests, but still old underneath! --- .../discourse/tests/helpers/component-test.js | 25 + .../discourse/tests/helpers/qunit-helpers.js | 2 + .../integration/components/ace-editor-test.js | 106 +- .../components/admin-report-test.js | 308 +-- .../components/badge-title-test.js | 66 +- .../integration/components/cook-text-test.js | 87 +- .../integration/components/d-button-test.js | 390 ++-- .../integration/components/d-editor-test.js | 1353 ++++++------ .../integration/components/d-icon-test.js | 56 +- .../integration/components/date-input-test.js | 96 +- .../components/date-time-input-range-test.js | 40 +- .../components/date-time-input-test.js | 122 +- .../components/highlighted-code-test.js | 68 +- .../components/html-safe-helper-test.js | 28 +- .../components/iframed-html-test.js | 45 +- .../components/image-uploader-test.js | 174 +- .../components/keyboard-shortcuts-test.js | 156 -- .../integration/components/load-more-test.js | 46 +- .../components/secret-value-list-test.js | 175 +- .../components/select-kit/api-test.js | 163 +- .../select-kit/category-chooser-test.js | 401 ++-- .../select-kit/category-drop-test.js | 540 ++--- .../components/select-kit/combo-box-test.js | 158 +- .../select-kit/dropdown-select-box-test.js | 173 +- .../select-kit/list-setting-test.js | 45 +- .../select-kit/mini-tag-chooser-test.js | 114 +- .../select-kit/multi-select-test.js | 69 +- .../select-kit/notifications-button-test.js | 79 +- .../select-kit/pinned-options-test.js | 74 +- .../select-kit/single-select-test.js | 529 ++--- .../components/select-kit/tag-drop-test.js | 109 +- .../topic-notifications-button-test.js | 91 +- .../topic-notifications-options-test.js | 107 +- .../select-kit/user-chooser-test.js | 61 +- .../components/simple-list-test.js | 164 +- .../integration/components/text-field-test.js | 177 +- .../integration/components/time-input-test.js | 96 +- .../components/user-selector-test.js | 90 +- .../integration/components/value-list-test.js | 270 +-- .../widgets/actions-summary-test.js | 54 +- .../integration/widgets/avatar-flair-test.js | 78 +- .../tests/integration/widgets/button-test.js | 122 +- .../widgets/default-notification-item-test.js | 125 +- .../widgets/hamburger-menu-test.js | 438 ++-- .../tests/integration/widgets/header-test.js | 154 +- .../integration/widgets/home-logo-test.js | 473 ++-- .../integration/widgets/post-links-test.js | 112 +- .../integration/widgets/post-menu-test.js | 88 +- .../integration/widgets/post-stream-test.js | 264 +-- .../tests/integration/widgets/post-test.js | 1938 +++++++++-------- .../integration/widgets/poster-name-test.js | 144 +- .../widgets/quick-access-item-test.js | 59 +- .../widgets/small-user-list-test.js | 46 +- .../widgets/topic-admin-menu-test.js | 94 +- .../widgets/topic-participant-test.js | 97 +- .../integration/widgets/topic-status-test.js | 76 +- .../integration/widgets/user-menu-test.js | 424 ++-- .../widgets/widget-dropdown-test.js | 572 ++--- .../tests/integration/widgets/widget-test.js | 772 +++---- .../group-membership-button-test.js | 1 + .../tests/unit/lib/keyboard-shortcuts-test.js | 156 ++ app/assets/javascripts/test-shims.js | 1 + vendor/assets/javascripts/ember-qunit.js | 3 + 63 files changed, 6816 insertions(+), 6328 deletions(-) delete mode 100644 app/assets/javascripts/discourse/tests/integration/components/keyboard-shortcuts-test.js rename app/assets/javascripts/discourse/tests/{integration => unit}/components/group-membership-button-test.js (97%) create mode 100644 app/assets/javascripts/discourse/tests/unit/lib/keyboard-shortcuts-test.js diff --git a/app/assets/javascripts/discourse/tests/helpers/component-test.js b/app/assets/javascripts/discourse/tests/helpers/component-test.js index 5509343240..e2edd2ead6 100644 --- a/app/assets/javascripts/discourse/tests/helpers/component-test.js +++ b/app/assets/javascripts/discourse/tests/helpers/component-test.js @@ -7,6 +7,31 @@ import Site from "discourse/models/site"; import Session from "discourse/models/session"; import { currentSettings } from "discourse/tests/helpers/site-settings"; import { test } from "qunit"; +import { TestModuleForComponent } from "@ember/test-helpers"; + +export function setupRenderingTest(hooks) { + let testModule; + + hooks.before(function () { + const name = this.moduleName.split("|").pop(); + testModule = new TestModuleForComponent(name, { + integration: true, + }); + }); + + hooks.beforeEach(function () { + testModule.setContext(this); + return testModule.setup(...arguments); + }); + + hooks.afterEach(function () { + return testModule.teardown(...arguments); + }); + + hooks.after(function () { + testModule = null; + }); +} export default function (name, opts) { opts = opts || {}; diff --git a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js index 7776186da1..09d8afa705 100644 --- a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js +++ b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js @@ -113,6 +113,8 @@ export function discourseModule(name, options) { this.siteSettings = currentSettings(); }); + this.moduleName = name; + options.call(this, hooks); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/ace-editor-test.js b/app/assets/javascripts/discourse/tests/integration/components/ace-editor-test.js index 0896344d47..83f94cc4c9 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/ace-editor-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/ace-editor-test.js @@ -1,53 +1,59 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import componentTest from "discourse/tests/helpers/component-test"; -import { moduleForComponent } from "ember-qunit"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForComponent("ace-editor", { integration: true }); +discourseModule("Integration | Component | ace-editor", function (hooks) { + setupRenderingTest(hooks); -componentTest("css editor", { - skip: true, - template: '{{ace-editor mode="css"}}', - test(assert) { - assert.expect(1); - assert.ok(queryAll(".ace_editor").length, "it renders the ace editor"); - }, -}); - -componentTest("html editor", { - skip: true, - template: '{{ace-editor mode="html" content="wat"}}', - test(assert) { - assert.expect(1); - assert.ok(queryAll(".ace_editor").length, "it renders the ace editor"); - }, -}); - -componentTest("sql editor", { - skip: true, - template: '{{ace-editor mode="sql" content="SELECT * FROM users"}}', - test(assert) { - assert.expect(1); - assert.ok(queryAll(".ace_editor").length, "it renders the ace editor"); - }, -}); - -componentTest("disabled editor", { - skip: true, - template: - '{{ace-editor mode="sql" content="SELECT * FROM users" disabled=true}}', - test(assert) { - const $ace = queryAll(".ace_editor"); - assert.expect(3); - assert.ok($ace.length, "it renders the ace editor"); - assert.equal( - $ace.parent().data().editor.getReadOnly(), - true, - "it sets ACE to read-only mode" - ); - assert.equal( - $ace.parent().attr("data-disabled"), - "true", - "ACE wrapper has `data-disabled` attribute set to true" - ); - }, + componentTest("css editor", { + skip: true, + template: '{{ace-editor mode="css"}}', + test(assert) { + assert.expect(1); + assert.ok(queryAll(".ace_editor").length, "it renders the ace editor"); + }, + }); + + componentTest("html editor", { + skip: true, + template: '{{ace-editor mode="html" content="wat"}}', + test(assert) { + assert.expect(1); + assert.ok(queryAll(".ace_editor").length, "it renders the ace editor"); + }, + }); + + componentTest("sql editor", { + skip: true, + template: '{{ace-editor mode="sql" content="SELECT * FROM users"}}', + test(assert) { + assert.expect(1); + assert.ok(queryAll(".ace_editor").length, "it renders the ace editor"); + }, + }); + + componentTest("disabled editor", { + skip: true, + template: + '{{ace-editor mode="sql" content="SELECT * FROM users" disabled=true}}', + test(assert) { + const $ace = queryAll(".ace_editor"); + assert.expect(3); + assert.ok($ace.length, "it renders the ace editor"); + assert.equal( + $ace.parent().data().editor.getReadOnly(), + true, + "it sets ACE to read-only mode" + ); + assert.equal( + $ace.parent().attr("data-disabled"), + "true", + "ACE wrapper has `data-disabled` attribute set to true" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/admin-report-test.js b/app/assets/javascripts/discourse/tests/integration/components/admin-report-test.js index 7951b66fdd..878c007942 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/admin-report-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/admin-report-test.js @@ -1,173 +1,177 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + exists, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import pretender from "discourse/tests/helpers/create-pretender"; import { click } from "@ember/test-helpers"; -moduleForComponent("admin-report", { - integration: true, -}); +discourseModule("Integration | Component | admin-report", function (hooks) { + setupRenderingTest(hooks); -componentTest("default", { - template: "{{admin-report dataSourceName='signups'}}", + componentTest("default", { + template: "{{admin-report dataSourceName='signups'}}", - async test(assert) { - assert.ok(exists(".admin-report.signups")); + async test(assert) { + assert.ok(exists(".admin-report.signups")); - assert.ok(exists(".admin-report.signups", "it defaults to table mode")); + assert.ok(exists(".admin-report.signups", "it defaults to table mode")); - assert.equal( - queryAll(".header .item.report").text().trim(), - "Signups", - "it has a title" - ); + assert.equal( + queryAll(".header .item.report").text().trim(), + "Signups", + "it has a title" + ); - assert.equal( - queryAll(".header .info").attr("data-tooltip"), - "New account registrations for this period", - "it has a description" - ); + assert.equal( + queryAll(".header .info").attr("data-tooltip"), + "New account registrations for this period", + "it has a description" + ); - assert.equal( - queryAll(".admin-report-table thead tr th:first-child .title") - .text() - .trim(), - "Day", - "it has col headers" - ); + assert.equal( + queryAll(".admin-report-table thead tr th:first-child .title") + .text() + .trim(), + "Day", + "it has col headers" + ); - assert.equal( - queryAll(".admin-report-table thead tr th:nth-child(2) .title") - .text() - .trim(), - "Count", - "it has col headers" - ); + assert.equal( + queryAll(".admin-report-table thead tr th:nth-child(2) .title") + .text() + .trim(), + "Count", + "it has col headers" + ); - assert.equal( - queryAll(".admin-report-table tbody tr:nth-child(1) td:nth-child(1)") - .text() - .trim(), - "June 16, 2018", - "it has rows" - ); + assert.equal( + queryAll(".admin-report-table tbody tr:nth-child(1) td:nth-child(1)") + .text() + .trim(), + "June 16, 2018", + "it has rows" + ); - assert.equal( - queryAll(".admin-report-table tbody tr:nth-child(1) td:nth-child(2)") - .text() - .trim(), - "12", - "it has rows" - ); + assert.equal( + queryAll(".admin-report-table tbody tr:nth-child(1) td:nth-child(2)") + .text() + .trim(), + "12", + "it has rows" + ); - assert.ok(exists(".total-row"), "it has totals"); + assert.ok(exists(".total-row"), "it has totals"); - await click(".admin-report-table-header.y .sort-btn"); + await click(".admin-report-table-header.y .sort-btn"); - assert.equal( - queryAll(".admin-report-table tbody tr:nth-child(1) td:nth-child(2)") - .text() - .trim(), - "7", - "it can sort rows" - ); - }, -}); + assert.equal( + queryAll(".admin-report-table tbody tr:nth-child(1) td:nth-child(2)") + .text() + .trim(), + "7", + "it can sort rows" + ); + }, + }); -componentTest("options", { - template: "{{admin-report dataSourceName='signups' reportOptions=options}}", + componentTest("options", { + template: "{{admin-report dataSourceName='signups' reportOptions=options}}", - beforeEach() { - this.set("options", { - table: { - perPage: 4, - total: false, - }, - }); - }, - - test(assert) { - assert.ok(exists(".pagination"), "it paginates the results"); - assert.equal( - queryAll(".pagination button").length, - 3, - "it creates the correct number of pages" - ); - - assert.notOk(exists(".totals-sample-table"), "it hides totals"); - }, -}); - -componentTest("switch modes", { - template: "{{admin-report dataSourceName='signups' showFilteringUI=true}}", - - async test(assert) { - await click(".mode-btn.chart"); - - assert.notOk(exists(".admin-report-table"), "it removes the table"); - assert.ok(exists(".admin-report-chart"), "it shows the chart"); - }, -}); - -componentTest("timeout", { - template: "{{admin-report dataSourceName='signups_timeout'}}", - - test(assert) { - assert.ok(exists(".alert-error.timeout"), "it displays a timeout error"); - }, -}); - -componentTest("no data", { - template: "{{admin-report dataSourceName='posts'}}", - - test(assert) { - assert.ok(exists(".no-data"), "it displays a no data alert"); - }, -}); - -componentTest("exception", { - template: "{{admin-report dataSourceName='signups_exception'}}", - - test(assert) { - assert.ok(exists(".alert-error.exception"), "it displays an error"); - }, -}); - -componentTest("rate limited", { - beforeEach() { - pretender.get("/admin/reports/bulk", () => { - return [ - 429, - { "Content-Type": "application/json" }, - { - errors: [ - "You’ve performed this action too many times. Please wait 10 seconds before trying again.", - ], - error_type: "rate_limit", - extras: { wait_seconds: 10 }, + beforeEach() { + this.set("options", { + table: { + perPage: 4, + total: false, }, - ]; - }); - }, + }); + }, - template: "{{admin-report dataSourceName='signups_rate_limited'}}", + test(assert) { + assert.ok(exists(".pagination"), "it paginates the results"); + assert.equal( + queryAll(".pagination button").length, + 3, + "it creates the correct number of pages" + ); - test(assert) { - assert.ok( - exists(".alert-error.rate-limited"), - "it displays a rate limited error" - ); - }, -}); - -componentTest("not found", { - template: "{{admin-report dataSourceName='not_found'}}", - - test(assert) { - assert.ok( - exists(".alert-error.not-found"), - "it displays a not found error" - ); - }, + assert.notOk(exists(".totals-sample-table"), "it hides totals"); + }, + }); + + componentTest("switch modes", { + template: "{{admin-report dataSourceName='signups' showFilteringUI=true}}", + + async test(assert) { + await click(".mode-btn.chart"); + + assert.notOk(exists(".admin-report-table"), "it removes the table"); + assert.ok(exists(".admin-report-chart"), "it shows the chart"); + }, + }); + + componentTest("timeout", { + template: "{{admin-report dataSourceName='signups_timeout'}}", + + test(assert) { + assert.ok(exists(".alert-error.timeout"), "it displays a timeout error"); + }, + }); + + componentTest("no data", { + template: "{{admin-report dataSourceName='posts'}}", + + test(assert) { + assert.ok(exists(".no-data"), "it displays a no data alert"); + }, + }); + + componentTest("exception", { + template: "{{admin-report dataSourceName='signups_exception'}}", + + test(assert) { + assert.ok(exists(".alert-error.exception"), "it displays an error"); + }, + }); + + componentTest("rate limited", { + beforeEach() { + pretender.get("/admin/reports/bulk", () => { + return [ + 429, + { "Content-Type": "application/json" }, + { + errors: [ + "You’ve performed this action too many times. Please wait 10 seconds before trying again.", + ], + error_type: "rate_limit", + extras: { wait_seconds: 10 }, + }, + ]; + }); + }, + + template: "{{admin-report dataSourceName='signups_rate_limited'}}", + + test(assert) { + assert.ok( + exists(".alert-error.rate-limited"), + "it displays a rate limited error" + ); + }, + }); + + componentTest("not found", { + template: "{{admin-report dataSourceName='not_found'}}", + + test(assert) { + assert.ok( + exists(".alert-error.not-found"), + "it displays a not found error" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/badge-title-test.js b/app/assets/javascripts/discourse/tests/integration/components/badge-title-test.js index 2c94d3c55c..8f8ee9e9ac 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/badge-title-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/badge-title-test.js @@ -1,40 +1,44 @@ -import { moduleForComponent } from "ember-qunit"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import EmberObject from "@ember/object"; import pretender from "discourse/tests/helpers/create-pretender"; import { click } from "@ember/test-helpers"; -moduleForComponent("badge-title", { integration: true }); +discourseModule("Integration | Component | badge-title", function (hooks) { + setupRenderingTest(hooks); -componentTest("badge title", { - template: - "{{badge-title selectableUserBadges=selectableUserBadges user=user}}", + componentTest("badge title", { + template: + "{{badge-title selectableUserBadges=selectableUserBadges user=user}}", - beforeEach() { - this.set("subject", selectKit()); - this.set("selectableUserBadges", [ - EmberObject.create({ - id: 0, - badge: { name: "(none)" }, - }), - EmberObject.create({ - id: 42, - badge_id: 102, - badge: { name: "Test" }, - }), - ]); - }, + beforeEach() { + this.set("subject", selectKit()); + this.set("selectableUserBadges", [ + EmberObject.create({ + id: 0, + badge: { name: "(none)" }, + }), + EmberObject.create({ + id: 42, + badge_id: 102, + badge: { name: "Test" }, + }), + ]); + }, - async test(assert) { - pretender.put("/u/eviltrout/preferences/badge_title", () => [ - 200, - { "Content-Type": "application/json" }, - {}, - ]); - await this.subject.expand(); - await this.subject.selectRowByValue(42); - await click(".btn"); - assert.equal(this.currentUser.title, "Test"); - }, + async test(assert) { + pretender.put("/u/eviltrout/preferences/badge_title", () => [ + 200, + { "Content-Type": "application/json" }, + {}, + ]); + await this.subject.expand(); + await this.subject.selectRowByValue(42); + await click(".btn"); + assert.equal(this.currentUser.title, "Test"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/cook-text-test.js b/app/assets/javascripts/discourse/tests/integration/components/cook-text-test.js index 5573570de5..0477ecb84f 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/cook-text-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/cook-text-test.js @@ -1,45 +1,54 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + queryAll, + discourseModule, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import pretender from "discourse/tests/helpers/create-pretender"; import { resetCache } from "pretty-text/upload-short-url"; -moduleForComponent("cook-text", { integration: true }); +discourseModule("Integration | Component | cook-text", function (hooks) { + setupRenderingTest(hooks); -componentTest("renders markdown", { - template: '{{cook-text "_foo_" class="post-body"}}', + componentTest("renders markdown", { + template: '{{cook-text "_foo_" class="post-body"}}', - test(assert) { - const html = queryAll(".post-body")[0].innerHTML.trim(); - assert.equal(html, "

foo

"); - }, -}); - -componentTest("resolves short URLs", { - template: `{{cook-text "![an image](upload://a.png)" class="post-body"}}`, - - beforeEach() { - pretender.post("/uploads/lookup-urls", () => { - return [ - 200, - { "Content-Type": "application/json" }, - [ - { - short_url: "upload://a.png", - url: "/images/avatar.png", - short_path: "/images/d-logo-sketch.png", - }, - ], - ]; - }); - }, - - afterEach() { - resetCache(); - }, - - test(assert) { - const html = queryAll(".post-body")[0].innerHTML.trim(); - assert.equal(html, '

an image

'); - }, + test(assert) { + const html = queryAll(".post-body")[0].innerHTML.trim(); + assert.equal(html, "

foo

"); + }, + }); + + componentTest("resolves short URLs", { + template: `{{cook-text "![an image](upload://a.png)" class="post-body"}}`, + + beforeEach() { + pretender.post("/uploads/lookup-urls", () => { + return [ + 200, + { "Content-Type": "application/json" }, + [ + { + short_url: "upload://a.png", + url: "/images/avatar.png", + short_path: "/images/d-logo-sketch.png", + }, + ], + ]; + }); + }, + + afterEach() { + resetCache(); + }, + + test(assert) { + const html = queryAll(".post-body")[0].innerHTML.trim(); + assert.equal( + html, + '

an image

' + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/d-button-test.js b/app/assets/javascripts/discourse/tests/integration/components/d-button-test.js index c8294dc50e..a79aeceb87 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/d-button-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/d-button-test.js @@ -1,191 +1,207 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; +import { + discourseModule, + exists, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; -moduleForComponent("d-button", { integration: true }); +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -componentTest("icon only button", { - template: '{{d-button icon="plus" tabindex="3"}}', +discourseModule("Integration | Component | d-button", function (hooks) { + setupRenderingTest(hooks); - test(assert) { - assert.ok( - queryAll("button.btn.btn-icon.no-text").length, - "it has all the classes" - ); - assert.ok(queryAll("button .d-icon.d-icon-plus").length, "it has the icon"); - assert.equal( - queryAll("button").attr("tabindex"), - "3", - "it has the tabindex" - ); - }, -}); - -componentTest("icon and text button", { - template: '{{d-button icon="plus" label="topic.create"}}', - - test(assert) { - assert.ok( - queryAll("button.btn.btn-icon-text").length, - "it has all the classes" - ); - assert.ok(queryAll("button .d-icon.d-icon-plus").length, "it has the icon"); - assert.ok( - queryAll("button span.d-button-label").length, - "it has the label" - ); - }, -}); - -componentTest("text only button", { - template: '{{d-button label="topic.create"}}', - - test(assert) { - assert.ok(queryAll("button.btn.btn-text").length, "it has all the classes"); - assert.ok( - queryAll("button span.d-button-label").length, - "it has the label" - ); - }, -}); - -componentTest("form attribute", { - template: '{{d-button form="login-form"}}', - - test(assert) { - assert.ok(exists("button[form=login-form]"), "it has the form attribute"); - }, -}); - -componentTest("link-styled button", { - template: '{{d-button display="link"}}', - - test(assert) { - assert.ok( - queryAll("button.btn-link:not(.btn)").length, - "it has the right classes" - ); - }, -}); - -componentTest("isLoading button", { - template: "{{d-button isLoading=isLoading}}", - - beforeEach() { - this.set("isLoading", true); - }, - - test(assert) { - assert.ok( - queryAll("button.is-loading .loading-icon").length, - "it has a spinner showing" - ); - assert.ok( - queryAll("button[disabled]").length, - "while loading the button is disabled" - ); - - this.set("isLoading", false); - - assert.notOk( - queryAll("button .loading-icon").length, - "it doesn't have a spinner showing" - ); - assert.ok( - queryAll("button:not([disabled])").length, - "while not loading the button is enabled" - ); - }, -}); - -componentTest("disabled button", { - template: "{{d-button disabled=disabled}}", - - beforeEach() { - this.set("disabled", true); - }, - - test(assert) { - assert.ok(queryAll("button[disabled]").length, "the button is disabled"); - - this.set("disabled", false); - - assert.ok( - queryAll("button:not([disabled])").length, - "the button is enabled" - ); - }, -}); - -componentTest("aria-label", { - template: - "{{d-button ariaLabel=ariaLabel translatedAriaLabel=translatedAriaLabel}}", - - beforeEach() { - I18n.translations[I18n.locale].js.test = { fooAriaLabel: "foo" }; - }, - - test(assert) { - this.set("ariaLabel", "test.fooAriaLabel"); - - assert.equal( - queryAll("button")[0].getAttribute("aria-label"), - I18n.t("test.fooAriaLabel") - ); - - this.setProperties({ - ariaLabel: null, - translatedAriaLabel: "bar", - }); - - assert.equal(queryAll("button")[0].getAttribute("aria-label"), "bar"); - }, -}); - -componentTest("title", { - template: "{{d-button title=title translatedTitle=translatedTitle}}", - - beforeEach() { - I18n.translations[I18n.locale].js.test = { fooTitle: "foo" }; - }, - - test(assert) { - this.set("title", "test.fooTitle"); - assert.equal( - queryAll("button")[0].getAttribute("title"), - I18n.t("test.fooTitle") - ); - - this.setProperties({ - title: null, - translatedTitle: "bar", - }); - - assert.equal(queryAll("button")[0].getAttribute("title"), "bar"); - }, -}); - -componentTest("label", { - template: "{{d-button label=label translatedLabel=translatedLabel}}", - - beforeEach() { - I18n.translations[I18n.locale].js.test = { fooLabel: "foo" }; - }, - - test(assert) { - this.set("label", "test.fooLabel"); - - assert.equal( - queryAll("button .d-button-label").text(), - I18n.t("test.fooLabel") - ); - - this.setProperties({ - label: null, - translatedLabel: "bar", - }); - - assert.equal(queryAll("button .d-button-label").text(), "bar"); - }, + componentTest("icon only button", { + template: '{{d-button icon="plus" tabindex="3"}}', + + test(assert) { + assert.ok( + queryAll("button.btn.btn-icon.no-text").length, + "it has all the classes" + ); + assert.ok( + queryAll("button .d-icon.d-icon-plus").length, + "it has the icon" + ); + assert.equal( + queryAll("button").attr("tabindex"), + "3", + "it has the tabindex" + ); + }, + }); + + componentTest("icon and text button", { + template: '{{d-button icon="plus" label="topic.create"}}', + + test(assert) { + assert.ok( + queryAll("button.btn.btn-icon-text").length, + "it has all the classes" + ); + assert.ok( + queryAll("button .d-icon.d-icon-plus").length, + "it has the icon" + ); + assert.ok( + queryAll("button span.d-button-label").length, + "it has the label" + ); + }, + }); + + componentTest("text only button", { + template: '{{d-button label="topic.create"}}', + + test(assert) { + assert.ok( + queryAll("button.btn.btn-text").length, + "it has all the classes" + ); + assert.ok( + queryAll("button span.d-button-label").length, + "it has the label" + ); + }, + }); + + componentTest("form attribute", { + template: '{{d-button form="login-form"}}', + + test(assert) { + assert.ok(exists("button[form=login-form]"), "it has the form attribute"); + }, + }); + + componentTest("link-styled button", { + template: '{{d-button display="link"}}', + + test(assert) { + assert.ok( + queryAll("button.btn-link:not(.btn)").length, + "it has the right classes" + ); + }, + }); + + componentTest("isLoading button", { + template: "{{d-button isLoading=isLoading}}", + + beforeEach() { + this.set("isLoading", true); + }, + + test(assert) { + assert.ok( + queryAll("button.is-loading .loading-icon").length, + "it has a spinner showing" + ); + assert.ok( + queryAll("button[disabled]").length, + "while loading the button is disabled" + ); + + this.set("isLoading", false); + + assert.notOk( + queryAll("button .loading-icon").length, + "it doesn't have a spinner showing" + ); + assert.ok( + queryAll("button:not([disabled])").length, + "while not loading the button is enabled" + ); + }, + }); + + componentTest("disabled button", { + template: "{{d-button disabled=disabled}}", + + beforeEach() { + this.set("disabled", true); + }, + + test(assert) { + assert.ok(queryAll("button[disabled]").length, "the button is disabled"); + + this.set("disabled", false); + + assert.ok( + queryAll("button:not([disabled])").length, + "the button is enabled" + ); + }, + }); + + componentTest("aria-label", { + template: + "{{d-button ariaLabel=ariaLabel translatedAriaLabel=translatedAriaLabel}}", + + beforeEach() { + I18n.translations[I18n.locale].js.test = { fooAriaLabel: "foo" }; + }, + + test(assert) { + this.set("ariaLabel", "test.fooAriaLabel"); + + assert.equal( + queryAll("button")[0].getAttribute("aria-label"), + I18n.t("test.fooAriaLabel") + ); + + this.setProperties({ + ariaLabel: null, + translatedAriaLabel: "bar", + }); + + assert.equal(queryAll("button")[0].getAttribute("aria-label"), "bar"); + }, + }); + + componentTest("title", { + template: "{{d-button title=title translatedTitle=translatedTitle}}", + + beforeEach() { + I18n.translations[I18n.locale].js.test = { fooTitle: "foo" }; + }, + + test(assert) { + this.set("title", "test.fooTitle"); + assert.equal( + queryAll("button")[0].getAttribute("title"), + I18n.t("test.fooTitle") + ); + + this.setProperties({ + title: null, + translatedTitle: "bar", + }); + + assert.equal(queryAll("button")[0].getAttribute("title"), "bar"); + }, + }); + + componentTest("label", { + template: "{{d-button label=label translatedLabel=translatedLabel}}", + + beforeEach() { + I18n.translations[I18n.locale].js.test = { fooLabel: "foo" }; + }, + + test(assert) { + this.set("label", "test.fooLabel"); + + assert.equal( + queryAll("button .d-button-label").text(), + I18n.t("test.fooLabel") + ); + + this.setProperties({ + label: null, + translatedLabel: "bar", + }); + + assert.equal(queryAll("button .d-button-label").text(), "bar"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js b/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js index 86ba4c800a..c116dab8af 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/d-editor-test.js @@ -1,9 +1,13 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; import I18n from "I18n"; import { next } from "@ember/runloop"; import { clearToolbarCallbacks } from "discourse/components/d-editor"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { withPluginApi } from "discourse/lib/plugin-api"; import formatTextWithSelection from "discourse/tests/helpers/d-editor-helper"; import { @@ -12,788 +16,805 @@ import { } from "discourse/tests/helpers/textarea-selection-helper"; import { fillIn, click } from "@ember/test-helpers"; -moduleForComponent("d-editor", { integration: true }); +discourseModule("Integration | Component | d-editor", function (hooks) { + setupRenderingTest(hooks); -componentTest("preview updates with markdown", { - template: "{{d-editor value=value}}", - - async test(assert) { - assert.ok(queryAll(".d-editor-button-bar").length); - await fillIn(".d-editor-input", "hello **world**"); - - assert.equal(this.value, "hello **world**"); - assert.equal( - queryAll(".d-editor-preview").html().trim(), - "

hello world

" - ); - }, -}); - -componentTest("preview sanitizes HTML", { - template: "{{d-editor value=value}}", - - async test(assert) { - await fillIn(".d-editor-input", `">`); - assert.equal(queryAll(".d-editor-preview").html().trim(), '

">

'); - }, -}); - -componentTest("updating the value refreshes the preview", { - template: "{{d-editor value=value}}", - - beforeEach() { - this.set("value", "evil trout"); - }, - - async test(assert) { - assert.equal( - queryAll(".d-editor-preview").html().trim(), - "

evil trout

" - ); - - await this.set("value", "zogstrip"); - assert.equal( - queryAll(".d-editor-preview").html().trim(), - "

zogstrip

" - ); - }, -}); - -function jumpEnd(textarea) { - textarea.selectionStart = textarea.value.length; - textarea.selectionEnd = textarea.value.length; - return textarea; -} - -function testCase(title, testFunc) { - componentTest(title, { + componentTest("preview updates with markdown", { template: "{{d-editor value=value}}", - beforeEach() { - this.set("value", "hello world."); - }, - test(assert) { - const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); - testFunc.call(this, assert, textarea); + + async test(assert) { + assert.ok(queryAll(".d-editor-button-bar").length); + await fillIn(".d-editor-input", "hello **world**"); + + assert.equal(this.value, "hello **world**"); + assert.equal( + queryAll(".d-editor-preview").html().trim(), + "

hello world

" + ); }, }); -} -function composerTestCase(title, testFunc) { - componentTest(title, { - template: "{{d-editor value=value composerEvents=true}}", - beforeEach() { - this.set("value", "hello world."); - }, + componentTest("preview sanitizes HTML", { + template: "{{d-editor value=value}}", - test(assert) { - const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); - testFunc.call(this, assert, textarea); + async test(assert) { + await fillIn(".d-editor-input", `">`); + assert.equal(queryAll(".d-editor-preview").html().trim(), '

">

'); }, }); -} -testCase(`selecting the space before a word`, async function ( - assert, - textarea -) { - textarea.selectionStart = 5; - textarea.selectionEnd = 7; + componentTest("updating the value refreshes the preview", { + template: "{{d-editor value=value}}", - await click(`button.bold`); + beforeEach() { + this.set("value", "evil trout"); + }, - assert.equal(this.value, `hello **w**orld.`); - assert.equal(textarea.selectionStart, 8); - assert.equal(textarea.selectionEnd, 9); -}); + async test(assert) { + assert.equal( + queryAll(".d-editor-preview").html().trim(), + "

evil trout

" + ); -testCase(`selecting the space after a word`, async function (assert, textarea) { - textarea.selectionStart = 0; - textarea.selectionEnd = 6; + await this.set("value", "zogstrip"); + assert.equal( + queryAll(".d-editor-preview").html().trim(), + "

zogstrip

" + ); + }, + }); - await click(`button.bold`); - - assert.equal(this.value, `**hello** world.`); - assert.equal(textarea.selectionStart, 2); - assert.equal(textarea.selectionEnd, 7); -}); - -testCase(`bold button with no selection`, async function (assert, textarea) { - await click(`button.bold`); - - const example = I18n.t(`composer.bold_text`); - assert.equal(this.value, `hello world.**${example}**`); - assert.equal(textarea.selectionStart, 14); - assert.equal(textarea.selectionEnd, 14 + example.length); -}); - -testCase(`bold button with a selection`, async function (assert, textarea) { - textarea.selectionStart = 6; - textarea.selectionEnd = 11; - - await click(`button.bold`); - assert.equal(this.value, `hello **world**.`); - assert.equal(textarea.selectionStart, 8); - assert.equal(textarea.selectionEnd, 13); - - await click(`button.bold`); - assert.equal(this.value, "hello world."); - assert.equal(textarea.selectionStart, 6); - assert.equal(textarea.selectionEnd, 11); -}); - -testCase(`bold with a multiline selection`, async function (assert, textarea) { - this.set("value", "hello\n\nworld\n\ntest."); - - textarea.selectionStart = 0; - textarea.selectionEnd = 12; - - await click(`button.bold`); - assert.equal(this.value, `**hello**\n\n**world**\n\ntest.`); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 20); - - await click(`button.bold`); - assert.equal(this.value, `hello\n\nworld\n\ntest.`); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 12); -}); - -testCase(`italic button with no selection`, async function (assert, textarea) { - await click(`button.italic`); - const example = I18n.t(`composer.italic_text`); - assert.equal(this.value, `hello world.*${example}*`); - - assert.equal(textarea.selectionStart, 13); - assert.equal(textarea.selectionEnd, 13 + example.length); -}); - -testCase(`italic button with a selection`, async function (assert, textarea) { - textarea.selectionStart = 6; - textarea.selectionEnd = 11; - - await click(`button.italic`); - assert.equal(this.value, `hello *world*.`); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 12); - - await click(`button.italic`); - assert.equal(this.value, "hello world."); - assert.equal(textarea.selectionStart, 6); - assert.equal(textarea.selectionEnd, 11); -}); - -testCase(`italic with a multiline selection`, async function ( - assert, - textarea -) { - this.set("value", "hello\n\nworld\n\ntest."); - - textarea.selectionStart = 0; - textarea.selectionEnd = 12; - - await click(`button.italic`); - assert.equal(this.value, `*hello*\n\n*world*\n\ntest.`); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 16); - - await click(`button.italic`); - assert.equal(this.value, `hello\n\nworld\n\ntest.`); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 12); -}); - -componentTest("advanced code", { - template: "{{d-editor value=value}}", - beforeEach() { - this.siteSettings.code_formatting_style = "4-spaces-indent"; - this.set( - "value", - ` -function xyz(x, y, z) { - if (y === z) { - return true; - } -} -` - ); - }, - - async test(assert) { - const textarea = queryAll("textarea.d-editor-input")[0]; - textarea.selectionStart = 0; + function jumpEnd(textarea) { + textarea.selectionStart = textarea.value.length; textarea.selectionEnd = textarea.value.length; + return textarea; + } - await click("button.code"); - assert.equal( - this.value, - ` - function xyz(x, y, z) { - if (y === z) { - return true; - } - } -` - ); - }, -}); + function testCase(title, testFunc) { + componentTest(title, { + template: "{{d-editor value=value}}", + beforeEach() { + this.set("value", "hello world."); + }, + test(assert) { + const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + testFunc.call(this, assert, textarea); + }, + }); + } -componentTest("code button", { - template: "{{d-editor value=value}}", - beforeEach() { - this.siteSettings.code_formatting_style = "4-spaces-indent"; - }, + function composerTestCase(title, testFunc) { + componentTest(title, { + template: "{{d-editor value=value composerEvents=true}}", + beforeEach() { + this.set("value", "hello world."); + }, - async test(assert) { - const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + test(assert) { + const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + testFunc.call(this, assert, textarea); + }, + }); + } - await click("button.code"); - assert.equal(this.value, ` ${I18n.t("composer.code_text")}`); + testCase(`selecting the space before a word`, async function ( + assert, + textarea + ) { + textarea.selectionStart = 5; + textarea.selectionEnd = 7; - this.set("value", "first line\n\nsecond line\n\nthird line"); + await click(`button.bold`); - textarea.selectionStart = 11; + assert.equal(this.value, `hello **w**orld.`); + assert.equal(textarea.selectionStart, 8); + assert.equal(textarea.selectionEnd, 9); + }); + + testCase(`selecting the space after a word`, async function ( + assert, + textarea + ) { + textarea.selectionStart = 0; + textarea.selectionEnd = 6; + + await click(`button.bold`); + + assert.equal(this.value, `**hello** world.`); + assert.equal(textarea.selectionStart, 2); + assert.equal(textarea.selectionEnd, 7); + }); + + testCase(`bold button with no selection`, async function (assert, textarea) { + await click(`button.bold`); + + const example = I18n.t(`composer.bold_text`); + assert.equal(this.value, `hello world.**${example}**`); + assert.equal(textarea.selectionStart, 14); + assert.equal(textarea.selectionEnd, 14 + example.length); + }); + + testCase(`bold button with a selection`, async function (assert, textarea) { + textarea.selectionStart = 6; textarea.selectionEnd = 11; - await click("button.code"); - assert.equal( - this.value, - `first line + await click(`button.bold`); + assert.equal(this.value, `hello **world**.`); + assert.equal(textarea.selectionStart, 8); + assert.equal(textarea.selectionEnd, 13); + + await click(`button.bold`); + assert.equal(this.value, "hello world."); + assert.equal(textarea.selectionStart, 6); + assert.equal(textarea.selectionEnd, 11); + }); + + testCase(`bold with a multiline selection`, async function ( + assert, + textarea + ) { + this.set("value", "hello\n\nworld\n\ntest."); + + textarea.selectionStart = 0; + textarea.selectionEnd = 12; + + await click(`button.bold`); + assert.equal(this.value, `**hello**\n\n**world**\n\ntest.`); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 20); + + await click(`button.bold`); + assert.equal(this.value, `hello\n\nworld\n\ntest.`); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 12); + }); + + testCase(`italic button with no selection`, async function ( + assert, + textarea + ) { + await click(`button.italic`); + const example = I18n.t(`composer.italic_text`); + assert.equal(this.value, `hello world.*${example}*`); + + assert.equal(textarea.selectionStart, 13); + assert.equal(textarea.selectionEnd, 13 + example.length); + }); + + testCase(`italic button with a selection`, async function (assert, textarea) { + textarea.selectionStart = 6; + textarea.selectionEnd = 11; + + await click(`button.italic`); + assert.equal(this.value, `hello *world*.`); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 12); + + await click(`button.italic`); + assert.equal(this.value, "hello world."); + assert.equal(textarea.selectionStart, 6); + assert.equal(textarea.selectionEnd, 11); + }); + + testCase(`italic with a multiline selection`, async function ( + assert, + textarea + ) { + this.set("value", "hello\n\nworld\n\ntest."); + + textarea.selectionStart = 0; + textarea.selectionEnd = 12; + + await click(`button.italic`); + assert.equal(this.value, `*hello*\n\n*world*\n\ntest.`); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 16); + + await click(`button.italic`); + assert.equal(this.value, `hello\n\nworld\n\ntest.`); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 12); + }); + + componentTest("advanced code", { + template: "{{d-editor value=value}}", + beforeEach() { + this.siteSettings.code_formatting_style = "4-spaces-indent"; + this.set( + "value", + ` + function xyz(x, y, z) { + if (y === z) { + return true; + } + } + ` + ); + }, + + async test(assert) { + const textarea = queryAll("textarea.d-editor-input")[0]; + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; + + await click("button.code"); + assert.equal( + this.value, + ` + function xyz(x, y, z) { + if (y === z) { + return true; + } + } + ` + ); + }, + }); + + componentTest("code button", { + template: "{{d-editor value=value}}", + beforeEach() { + this.siteSettings.code_formatting_style = "4-spaces-indent"; + }, + + async test(assert) { + const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + + await click("button.code"); + assert.equal(this.value, ` ${I18n.t("composer.code_text")}`); + + this.set("value", "first line\n\nsecond line\n\nthird line"); + + textarea.selectionStart = 11; + textarea.selectionEnd = 11; + + await click("button.code"); + assert.equal( + this.value, + `first line ${I18n.t("composer.code_text")} second line third line` - ); + ); - this.set("value", "first line\n\nsecond line\n\nthird line"); + this.set("value", "first line\n\nsecond line\n\nthird line"); - await click("button.code"); - assert.equal( - this.value, - `first line + await click("button.code"); + assert.equal( + this.value, + `first line second line third line\`${I18n.t("composer.code_title")}\`` - ); - this.set("value", "first line\n\nsecond line\n\nthird line"); + ); + this.set("value", "first line\n\nsecond line\n\nthird line"); - textarea.selectionStart = 5; - textarea.selectionEnd = 5; + textarea.selectionStart = 5; + textarea.selectionEnd = 5; - await click("button.code"); - assert.equal( - this.value, - `first\`${I18n.t("composer.code_title")}\` line + await click("button.code"); + assert.equal( + this.value, + `first\`${I18n.t("composer.code_title")}\` line second line third line` - ); - this.set("value", "first line\n\nsecond line\n\nthird line"); + ); + this.set("value", "first line\n\nsecond line\n\nthird line"); - textarea.selectionStart = 6; - textarea.selectionEnd = 10; + textarea.selectionStart = 6; + textarea.selectionEnd = 10; - await click("button.code"); - assert.equal(this.value, "first `line`\n\nsecond line\n\nthird line"); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 11); + await click("button.code"); + assert.equal(this.value, "first `line`\n\nsecond line\n\nthird line"); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 11); - await click("button.code"); - assert.equal(this.value, "first line\n\nsecond line\n\nthird line"); - assert.equal(textarea.selectionStart, 6); - assert.equal(textarea.selectionEnd, 10); + await click("button.code"); + assert.equal(this.value, "first line\n\nsecond line\n\nthird line"); + assert.equal(textarea.selectionStart, 6); + assert.equal(textarea.selectionEnd, 10); - textarea.selectionStart = 0; - textarea.selectionEnd = 23; + textarea.selectionStart = 0; + textarea.selectionEnd = 23; - await click("button.code"); - assert.equal(this.value, " first line\n\n second line\n\nthird line"); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 31); + await click("button.code"); + assert.equal( + this.value, + " first line\n\n second line\n\nthird line" + ); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 31); - await click("button.code"); - assert.equal(this.value, "first line\n\nsecond line\n\nthird line"); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 23); - }, -}); + await click("button.code"); + assert.equal(this.value, "first line\n\nsecond line\n\nthird line"); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 23); + }, + }); -componentTest("code fences", { - template: "{{d-editor value=value}}", - beforeEach() { - this.set("value", ""); - }, + componentTest("code fences", { + template: "{{d-editor value=value}}", + beforeEach() { + this.set("value", ""); + }, - async test(assert) { - const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + async test(assert) { + const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); - await click("button.code"); - assert.equal( - this.value, - `\`\`\` + await click("button.code"); + assert.equal( + this.value, + `\`\`\` ${I18n.t("composer.paste_code_text")} \`\`\`` - ); + ); - assert.equal(textarea.selectionStart, 4); - assert.equal(textarea.selectionEnd, 27); + assert.equal(textarea.selectionStart, 4); + assert.equal(textarea.selectionEnd, 27); - this.set("value", "first line\nsecond line\nthird line"); + this.set("value", "first line\nsecond line\nthird line"); - textarea.selectionStart = 0; - textarea.selectionEnd = textarea.value.length; + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; - await click("button.code"); + await click("button.code"); - assert.equal( - this.value, - `\`\`\` + assert.equal( + this.value, + `\`\`\` first line second line third line \`\`\` ` - ); + ); - assert.equal(textarea.selectionStart, textarea.value.length); - assert.equal(textarea.selectionEnd, textarea.value.length); + assert.equal(textarea.selectionStart, textarea.value.length); + assert.equal(textarea.selectionEnd, textarea.value.length); - this.set("value", "first line\nsecond line\nthird line"); + this.set("value", "first line\nsecond line\nthird line"); - textarea.selectionStart = 0; - textarea.selectionEnd = 0; + textarea.selectionStart = 0; + textarea.selectionEnd = 0; - await click("button.code"); + await click("button.code"); - assert.equal( - this.value, - `\`${I18n.t("composer.code_title")}\`first line + assert.equal( + this.value, + `\`${I18n.t("composer.code_title")}\`first line second line third line` - ); + ); - assert.equal(textarea.selectionStart, 1); - assert.equal( - textarea.selectionEnd, - I18n.t("composer.code_title").length + 1 - ); + assert.equal(textarea.selectionStart, 1); + assert.equal( + textarea.selectionEnd, + I18n.t("composer.code_title").length + 1 + ); - this.set("value", "first line\nsecond line\nthird line"); + this.set("value", "first line\nsecond line\nthird line"); - textarea.selectionStart = 0; - textarea.selectionEnd = 10; + textarea.selectionStart = 0; + textarea.selectionEnd = 10; - await click("button.code"); + await click("button.code"); - assert.equal( - this.value, - `\`first line\` + assert.equal( + this.value, + `\`first line\` second line third line` - ); + ); - assert.equal(textarea.selectionStart, 1); - assert.equal(textarea.selectionEnd, 11); + assert.equal(textarea.selectionStart, 1); + assert.equal(textarea.selectionEnd, 11); - this.set("value", "first line\nsecond line\nthird line"); + this.set("value", "first line\nsecond line\nthird line"); - textarea.selectionStart = 0; - textarea.selectionEnd = 23; + textarea.selectionStart = 0; + textarea.selectionEnd = 23; - await click("button.code"); + await click("button.code"); - assert.equal( - this.value, - `\`\`\` + assert.equal( + this.value, + `\`\`\` first line second line \`\`\` third line` - ); + ); - assert.equal(textarea.selectionStart, 30); - assert.equal(textarea.selectionEnd, 30); + assert.equal(textarea.selectionStart, 30); + assert.equal(textarea.selectionEnd, 30); - this.set("value", "first line\nsecond line\nthird line"); + this.set("value", "first line\nsecond line\nthird line"); + textarea.selectionStart = 6; + textarea.selectionEnd = 17; + + await click("button.code"); + + assert.equal( + this.value, + `first \n\`\`\`\nline\nsecond\n\`\`\`\n line\nthird line` + ); + + assert.equal(textarea.selectionStart, 27); + assert.equal(textarea.selectionEnd, 27); + }, + }); + + componentTest("quote button - empty lines", { + template: "{{d-editor value=value composerEvents=true}}", + beforeEach() { + this.set("value", "one\n\ntwo\n\nthree"); + }, + async test(assert) { + const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + + textarea.selectionStart = 0; + + await click("button.quote"); + + assert.equal(this.value, "> one\n> \n> two\n> \n> three"); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 25); + + await click("button.quote"); + assert.equal(this.value, "one\n\ntwo\n\nthree"); + }, + }); + + componentTest("quote button - selecting empty lines", { + template: "{{d-editor value=value composerEvents=true}}", + beforeEach() { + this.set("value", "one\n\n\n\ntwo"); + }, + async test(assert) { + const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + + textarea.selectionStart = 6; + textarea.selectionEnd = 10; + + await click("button.quote"); + assert.equal(this.value, "one\n\n\n> \n> two"); + }, + }); + + testCase("quote button", async function (assert, textarea) { textarea.selectionStart = 6; - textarea.selectionEnd = 17; + textarea.selectionEnd = 9; - await click("button.code"); + await click("button.quote"); + assert.equal(this.value, "hello\n\n> wor\n\nld."); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 12); - assert.equal( - this.value, - `first \n\`\`\`\nline\nsecond\n\`\`\`\n line\nthird line` - ); + await click("button.quote"); - assert.equal(textarea.selectionStart, 27); - assert.equal(textarea.selectionEnd, 27); - }, -}); + assert.equal(this.value, "hello\n\nwor\n\nld."); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 10); -componentTest("quote button - empty lines", { - template: "{{d-editor value=value composerEvents=true}}", - beforeEach() { - this.set("value", "one\n\ntwo\n\nthree"); - }, - async test(assert) { - const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + textarea.selectionStart = 15; + textarea.selectionEnd = 15; + + await click("button.quote"); + assert.equal(this.value, "hello\n\nwor\n\nld.\n\n> Blockquote"); + }); + + testCase(`bullet button with no selection`, async function ( + assert, + textarea + ) { + const example = I18n.t("composer.list_item"); + + await click(`button.bullet`); + assert.equal(this.value, `hello world.\n\n* ${example}`); + assert.equal(textarea.selectionStart, 14); + assert.equal(textarea.selectionEnd, 16 + example.length); + + await click(`button.bullet`); + assert.equal(this.value, `hello world.\n\n${example}`); + }); + + testCase(`bullet button with a selection`, async function (assert, textarea) { + textarea.selectionStart = 6; + textarea.selectionEnd = 11; + + await click(`button.bullet`); + assert.equal(this.value, `hello\n\n* world\n\n.`); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 14); + + await click(`button.bullet`); + assert.equal(this.value, `hello\n\nworld\n\n.`); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 12); + }); + + testCase(`bullet button with a multiple line selection`, async function ( + assert, + textarea + ) { + this.set("value", "* Hello\n\nWorld\n\nEvil"); textarea.selectionStart = 0; + textarea.selectionEnd = 20; - await click("button.quote"); - - assert.equal(this.value, "> one\n> \n> two\n> \n> three"); + await click(`button.bullet`); + assert.equal(this.value, "Hello\n\nWorld\n\nEvil"); assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 25); + assert.equal(textarea.selectionEnd, 18); - await click("button.quote"); - assert.equal(this.value, "one\n\ntwo\n\nthree"); - }, -}); + await click(`button.bullet`); + assert.equal(this.value, "* Hello\n\n* World\n\n* Evil"); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 24); + }); -componentTest("quote button - selecting empty lines", { - template: "{{d-editor value=value composerEvents=true}}", - beforeEach() { - this.set("value", "one\n\n\n\ntwo"); - }, - async test(assert) { - const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]); + testCase(`list button with no selection`, async function (assert, textarea) { + const example = I18n.t("composer.list_item"); + await click(`button.list`); + assert.equal(this.value, `hello world.\n\n1. ${example}`); + assert.equal(textarea.selectionStart, 14); + assert.equal(textarea.selectionEnd, 17 + example.length); + + await click(`button.list`); + assert.equal(this.value, `hello world.\n\n${example}`); + assert.equal(textarea.selectionStart, 14); + assert.equal(textarea.selectionEnd, 14 + example.length); + }); + + testCase(`list button with a selection`, async function (assert, textarea) { textarea.selectionStart = 6; - textarea.selectionEnd = 10; + textarea.selectionEnd = 11; - await click("button.quote"); - assert.equal(this.value, "one\n\n\n> \n> two"); - }, -}); + await click(`button.list`); + assert.equal(this.value, `hello\n\n1. world\n\n.`); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 15); -testCase("quote button", async function (assert, textarea) { - textarea.selectionStart = 6; - textarea.selectionEnd = 9; + await click(`button.list`); + assert.equal(this.value, `hello\n\nworld\n\n.`); + assert.equal(textarea.selectionStart, 7); + assert.equal(textarea.selectionEnd, 12); + }); - await click("button.quote"); - assert.equal(this.value, "hello\n\n> wor\n\nld."); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 12); + testCase(`list button with line sequence`, async function (assert, textarea) { + this.set("value", "Hello\n\nWorld\n\nEvil"); - await click("button.quote"); + textarea.selectionStart = 0; + textarea.selectionEnd = 18; - assert.equal(this.value, "hello\n\nwor\n\nld."); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 10); + await click(`button.list`); + assert.equal(this.value, "1. Hello\n\n2. World\n\n3. Evil"); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 27); - textarea.selectionStart = 15; - textarea.selectionEnd = 15; + await click(`button.list`); + assert.equal(this.value, "Hello\n\nWorld\n\nEvil"); + assert.equal(textarea.selectionStart, 0); + assert.equal(textarea.selectionEnd, 18); + }); - await click("button.quote"); - assert.equal(this.value, "hello\n\nwor\n\nld.\n\n> Blockquote"); -}); + componentTest("clicking the toggle-direction changes dir from ltr to rtl", { + template: "{{d-editor value=value}}", + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + this.siteSettings.default_locale = "en_US"; + }, -testCase(`bullet button with no selection`, async function (assert, textarea) { - const example = I18n.t("composer.list_item"); + async test(assert) { + const textarea = queryAll("textarea.d-editor-input"); + await click("button.toggle-direction"); + assert.equal(textarea.attr("dir"), "rtl"); + }, + }); - await click(`button.bullet`); - assert.equal(this.value, `hello world.\n\n* ${example}`); - assert.equal(textarea.selectionStart, 14); - assert.equal(textarea.selectionEnd, 16 + example.length); + componentTest("clicking the toggle-direction changes dir from ltr to rtl", { + template: "{{d-editor value=value}}", + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + this.siteSettings.default_locale = "en_US"; + }, - await click(`button.bullet`); - assert.equal(this.value, `hello world.\n\n${example}`); -}); + async test(assert) { + const textarea = queryAll("textarea.d-editor-input"); + textarea.attr("dir", "ltr"); + await click("button.toggle-direction"); + assert.equal(textarea.attr("dir"), "rtl"); + }, + }); -testCase(`bullet button with a selection`, async function (assert, textarea) { - textarea.selectionStart = 6; - textarea.selectionEnd = 11; + testCase(`doesn't jump to bottom with long text`, async function ( + assert, + textarea + ) { + let longText = "hello world."; + for (let i = 0; i < 8; i++) { + longText = longText + longText; + } + this.set("value", longText); - await click(`button.bullet`); - assert.equal(this.value, `hello\n\n* world\n\n.`); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 14); + $(textarea).scrollTop(0); + textarea.selectionStart = 3; + textarea.selectionEnd = 3; - await click(`button.bullet`); - assert.equal(this.value, `hello\n\nworld\n\n.`); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 12); -}); + await click("button.bold"); + assert.equal($(textarea).scrollTop(), 0, "it stays scrolled up"); + }); -testCase(`bullet button with a multiple line selection`, async function ( - assert, - textarea -) { - this.set("value", "* Hello\n\nWorld\n\nEvil"); - - textarea.selectionStart = 0; - textarea.selectionEnd = 20; - - await click(`button.bullet`); - assert.equal(this.value, "Hello\n\nWorld\n\nEvil"); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 18); - - await click(`button.bullet`); - assert.equal(this.value, "* Hello\n\n* World\n\n* Evil"); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 24); -}); - -testCase(`list button with no selection`, async function (assert, textarea) { - const example = I18n.t("composer.list_item"); - - await click(`button.list`); - assert.equal(this.value, `hello world.\n\n1. ${example}`); - assert.equal(textarea.selectionStart, 14); - assert.equal(textarea.selectionEnd, 17 + example.length); - - await click(`button.list`); - assert.equal(this.value, `hello world.\n\n${example}`); - assert.equal(textarea.selectionStart, 14); - assert.equal(textarea.selectionEnd, 14 + example.length); -}); - -testCase(`list button with a selection`, async function (assert, textarea) { - textarea.selectionStart = 6; - textarea.selectionEnd = 11; - - await click(`button.list`); - assert.equal(this.value, `hello\n\n1. world\n\n.`); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 15); - - await click(`button.list`); - assert.equal(this.value, `hello\n\nworld\n\n.`); - assert.equal(textarea.selectionStart, 7); - assert.equal(textarea.selectionEnd, 12); -}); - -testCase(`list button with line sequence`, async function (assert, textarea) { - this.set("value", "Hello\n\nWorld\n\nEvil"); - - textarea.selectionStart = 0; - textarea.selectionEnd = 18; - - await click(`button.list`); - assert.equal(this.value, "1. Hello\n\n2. World\n\n3. Evil"); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 27); - - await click(`button.list`); - assert.equal(this.value, "Hello\n\nWorld\n\nEvil"); - assert.equal(textarea.selectionStart, 0); - assert.equal(textarea.selectionEnd, 18); -}); - -componentTest("clicking the toggle-direction changes dir from ltr to rtl", { - template: "{{d-editor value=value}}", - beforeEach() { - this.siteSettings.support_mixed_text_direction = true; - this.siteSettings.default_locale = "en_US"; - }, - - async test(assert) { - const textarea = queryAll("textarea.d-editor-input"); - await click("button.toggle-direction"); - assert.equal(textarea.attr("dir"), "rtl"); - }, -}); - -componentTest("clicking the toggle-direction changes dir from ltr to rtl", { - template: "{{d-editor value=value}}", - beforeEach() { - this.siteSettings.support_mixed_text_direction = true; - this.siteSettings.default_locale = "en_US"; - }, - - async test(assert) { - const textarea = queryAll("textarea.d-editor-input"); - textarea.attr("dir", "ltr"); - await click("button.toggle-direction"); - assert.equal(textarea.attr("dir"), "rtl"); - }, -}); - -testCase(`doesn't jump to bottom with long text`, async function ( - assert, - textarea -) { - let longText = "hello world."; - for (let i = 0; i < 8; i++) { - longText = longText + longText; - } - this.set("value", longText); - - $(textarea).scrollTop(0); - textarea.selectionStart = 3; - textarea.selectionEnd = 3; - - await click("button.bold"); - assert.equal($(textarea).scrollTop(), 0, "it stays scrolled up"); -}); - -componentTest("emoji", { - template: "{{d-editor value=value}}", - beforeEach() { - // Test adding a custom button - withPluginApi("0.1", (api) => { - api.onToolbarCreate((toolbar) => { - toolbar.addButton({ - id: "emoji", - group: "extras", - icon: "far-smile", - action: () => toolbar.context.send("emoji"), + componentTest("emoji", { + template: "{{d-editor value=value}}", + beforeEach() { + // Test adding a custom button + withPluginApi("0.1", (api) => { + api.onToolbarCreate((toolbar) => { + toolbar.addButton({ + id: "emoji", + group: "extras", + icon: "far-smile", + action: () => toolbar.context.send("emoji"), + }); }); }); - }); - this.set("value", "hello world."); - }, - - afterEach() { - clearToolbarCallbacks(); - }, - - async test(assert) { - jumpEnd(queryAll("textarea.d-editor-input")[0]); - await click("button.emoji"); - - await click( - '.emoji-picker .section[data-section="smileys_&_emotion"] img.emoji[title="grinning"]' - ); - assert.equal(this.value, "hello world. :grinning:"); - }, -}); - -testCase("replace-text event by default", async function (assert) { - this.set("value", "red green blue"); - - await this.container - .lookup("service:app-events") - .trigger("composer:replace-text", "green", "yellow"); - - assert.equal(this.value, "red green blue"); -}); - -composerTestCase("replace-text event for composer", async function (assert) { - this.set("value", "red green blue"); - - await this.container - .lookup("service:app-events") - .trigger("composer:replace-text", "green", "yellow"); - - assert.equal(this.value, "red yellow blue"); -}); - -function paste(element, text) { - let e = new Event("paste"); - e.clipboardData = { getData: () => text }; - element.dispatchEvent(e); -} - -componentTest("paste table", { - template: "{{d-editor value=value composerEvents=true}}", - beforeEach() { - this.set("value", ""); - this.siteSettings.enable_rich_text_paste = true; - }, - - async test(assert) { - let element = queryAll(".d-editor")[0]; - await paste(element, "\ta\tb\n1\t2\t3"); - assert.equal(this.value, "||a|b|\n|---|---|---|\n|1|2|3|\n"); - }, -}); - -(() => { - // Tests to check cursor/selection after replace-text event. - const BEFORE = "red green blue"; - const NEEDLE = "green"; - const REPLACE = "yellow"; - const AFTER = BEFORE.replace(NEEDLE, REPLACE); - - const CASES = [ - { - description: "cursor at start remains there", - before: [0, 0], - after: [0, 0], + this.set("value", "hello world."); }, - { - description: "cursor before needle becomes cursor before replacement", - before: [BEFORE.indexOf(NEEDLE), 0], - after: [AFTER.indexOf(REPLACE), 0], - }, - { - description: "cursor at needle start + 1 moves behind replacement", - before: [BEFORE.indexOf(NEEDLE) + 1, 0], - after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0], - }, - { - description: "cursor at needle end - 1 stays behind replacement", - before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length - 1, 0], - after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0], - }, - { - description: "cursor behind needle becomes cursor behind replacement", - before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length, 0], - after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0], - }, - { - description: "cursor at end remains there", - before: [BEFORE.length, 0], - after: [AFTER.length, 0], - }, - { - description: - "selection spanning needle start becomes selection until replacement start", - before: [BEFORE.indexOf(NEEDLE) - 1, 2], - after: [AFTER.indexOf(REPLACE) - 1, 1], - }, - { - description: - "selection spanning needle end becomes selection from replacement end", - before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length - 1, 2], - after: [AFTER.indexOf(REPLACE) + REPLACE.length, 1], - }, - { - description: - "selection spanning needle becomes selection spanning replacement", - before: [BEFORE.indexOf(NEEDLE) - 1, NEEDLE.length + 2], - after: [AFTER.indexOf(REPLACE) - 1, REPLACE.length + 2], - }, - { - description: "complete selection remains complete", - before: [0, BEFORE.length], - after: [0, AFTER.length], - }, - ]; - for (let i = 0; i < CASES.length; i++) { - const CASE = CASES[i]; - // prettier-ignore - composerTestCase(`replace-text event: ${CASE.description}`, async function( // eslint-disable-line no-loop-func - assert, - textarea - ) { - this.set("value", BEFORE); + afterEach() { + clearToolbarCallbacks(); + }, - await focus(textarea); + async test(assert) { + jumpEnd(queryAll("textarea.d-editor-input")[0]); + await click("button.emoji"); - assert.ok(textarea.value === BEFORE); + await click( + '.emoji-picker .section[data-section="smileys_&_emotion"] img.emoji[title="grinning"]' + ); + assert.equal(this.value, "hello world. :grinning:"); + }, + }); - const [start, len] = CASE.before; - setTextareaSelection(textarea, start, start + len); + testCase("replace-text event by default", async function (assert) { + this.set("value", "red green blue"); - this.container - .lookup("service:app-events") - .trigger("composer:replace-text", "green", "yellow", { forceFocus: true }); + await this.container + .lookup("service:app-events") + .trigger("composer:replace-text", "green", "yellow"); - next(() => { - let expect = formatTextWithSelection(AFTER, CASE.after); - let actual = formatTextWithSelection( - this.value, - getTextareaSelection(textarea) - ); - assert.equal(actual, expect); - }); - }); + assert.equal(this.value, "red green blue"); + }); + + composerTestCase("replace-text event for composer", async function (assert) { + this.set("value", "red green blue"); + + await this.container + .lookup("service:app-events") + .trigger("composer:replace-text", "green", "yellow"); + + assert.equal(this.value, "red yellow blue"); + }); + + function paste(element, text) { + let e = new Event("paste"); + e.clipboardData = { getData: () => text }; + element.dispatchEvent(e); } -})(); + + componentTest("paste table", { + template: "{{d-editor value=value composerEvents=true}}", + beforeEach() { + this.set("value", ""); + this.siteSettings.enable_rich_text_paste = true; + }, + + async test(assert) { + let element = queryAll(".d-editor")[0]; + await paste(element, "\ta\tb\n1\t2\t3"); + assert.equal(this.value, "||a|b|\n|---|---|---|\n|1|2|3|\n"); + }, + }); + + (() => { + // Tests to check cursor/selection after replace-text event. + const BEFORE = "red green blue"; + const NEEDLE = "green"; + const REPLACE = "yellow"; + const AFTER = BEFORE.replace(NEEDLE, REPLACE); + + const CASES = [ + { + description: "cursor at start remains there", + before: [0, 0], + after: [0, 0], + }, + { + description: "cursor before needle becomes cursor before replacement", + before: [BEFORE.indexOf(NEEDLE), 0], + after: [AFTER.indexOf(REPLACE), 0], + }, + { + description: "cursor at needle start + 1 moves behind replacement", + before: [BEFORE.indexOf(NEEDLE) + 1, 0], + after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0], + }, + { + description: "cursor at needle end - 1 stays behind replacement", + before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length - 1, 0], + after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0], + }, + { + description: "cursor behind needle becomes cursor behind replacement", + before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length, 0], + after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0], + }, + { + description: "cursor at end remains there", + before: [BEFORE.length, 0], + after: [AFTER.length, 0], + }, + { + description: + "selection spanning needle start becomes selection until replacement start", + before: [BEFORE.indexOf(NEEDLE) - 1, 2], + after: [AFTER.indexOf(REPLACE) - 1, 1], + }, + { + description: + "selection spanning needle end becomes selection from replacement end", + before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length - 1, 2], + after: [AFTER.indexOf(REPLACE) + REPLACE.length, 1], + }, + { + description: + "selection spanning needle becomes selection spanning replacement", + before: [BEFORE.indexOf(NEEDLE) - 1, NEEDLE.length + 2], + after: [AFTER.indexOf(REPLACE) - 1, REPLACE.length + 2], + }, + { + description: "complete selection remains complete", + before: [0, BEFORE.length], + after: [0, AFTER.length], + }, + ]; + + for (let i = 0; i < CASES.length; i++) { + const CASE = CASES[i]; + // prettier-ignore + composerTestCase(`replace-text event: ${CASE.description}`, async function( // eslint-disable-line no-loop-func + assert, + textarea + ) { + this.set("value", BEFORE); + + await focus(textarea); + + assert.ok(textarea.value === BEFORE); + + const [start, len] = CASE.before; + setTextareaSelection(textarea, start, start + len); + + this.container + .lookup("service:app-events") + .trigger("composer:replace-text", "green", "yellow", { forceFocus: true }); + + next(() => { + let expect = formatTextWithSelection(AFTER, CASE.after); + let actual = formatTextWithSelection( + this.value, + getTextareaSelection(textarea) + ); + assert.equal(actual, expect); + }); + }); + } + })(); +}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/d-icon-test.js b/app/assets/javascripts/discourse/tests/integration/components/d-icon-test.js index 14091f972a..4f1f69ee0d 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/d-icon-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/d-icon-test.js @@ -1,29 +1,35 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForComponent("d-icon", { integration: true }); +discourseModule("Integration | Component | d-icon", function (hooks) { + setupRenderingTest(hooks); -componentTest("default", { - template: '
{{d-icon "bars"}}
', + componentTest("default", { + template: '
{{d-icon "bars"}}
', - test(assert) { - const html = queryAll(".test").html().trim(); - assert.equal( - html, - '' - ); - }, -}); - -componentTest("with replacement", { - template: '
{{d-icon "d-watching"}}
', - - test(assert) { - const html = queryAll(".test").html().trim(); - assert.equal( - html, - '' - ); - }, + test(assert) { + const html = queryAll(".test").html().trim(); + assert.equal( + html, + '' + ); + }, + }); + + componentTest("with replacement", { + template: '
{{d-icon "d-watching"}}
', + + test(assert) { + const html = queryAll(".test").html().trim(); + assert.equal( + html, + '' + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/date-input-test.js b/app/assets/javascripts/discourse/tests/integration/components/date-input-test.js index a601658848..abc1246b0e 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/date-input-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/date-input-test.js @@ -1,10 +1,12 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForComponent("date-input", { integration: true }); - function dateInput() { return queryAll(".date-picker"); } @@ -23,46 +25,50 @@ function noop() {} const DEFAULT_DATE = moment("2019-01-29"); -componentTest("default", { - template: `{{date-input date=date}}`, +discourseModule("Integration | Component | date-input", function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.setProperties({ date: DEFAULT_DATE }); - }, + componentTest("default", { + template: `{{date-input date=date}}`, - test(assert) { - assert.equal(dateInput().val(), "January 29, 2019"); - }, -}); - -componentTest("prevents mutations", { - template: `{{date-input date=date onChange=onChange}}`, - - beforeEach() { - this.setProperties({ date: DEFAULT_DATE }); - this.set("onChange", noop); - }, - - async test(assert) { - await click(dateInput()); - await pika(2019, 0, 2); - - assert.ok(this.date.isSame(DEFAULT_DATE)); - }, -}); - -componentTest("allows mutations through actions", { - template: `{{date-input date=date onChange=onChange}}`, - - beforeEach() { - this.setProperties({ date: DEFAULT_DATE }); - this.set("onChange", setDate); - }, - - async test(assert) { - await click(dateInput()); - await pika(2019, 0, 2); - - assert.ok(this.date.isSame(moment("2019-01-02"))); - }, + beforeEach() { + this.setProperties({ date: DEFAULT_DATE }); + }, + + test(assert) { + assert.equal(dateInput().val(), "January 29, 2019"); + }, + }); + + componentTest("prevents mutations", { + template: `{{date-input date=date onChange=onChange}}`, + + beforeEach() { + this.setProperties({ date: DEFAULT_DATE }); + this.set("onChange", noop); + }, + + async test(assert) { + await click(dateInput()); + await pika(2019, 0, 2); + + assert.ok(this.date.isSame(DEFAULT_DATE)); + }, + }); + + componentTest("allows mutations through actions", { + template: `{{date-input date=date onChange=onChange}}`, + + beforeEach() { + this.setProperties({ date: DEFAULT_DATE }); + this.set("onChange", setDate); + }, + + async test(assert) { + await click(dateInput()); + await pika(2019, 0, 2); + + assert.ok(this.date.isSame(moment("2019-01-02"))); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/date-time-input-range-test.js b/app/assets/javascripts/discourse/tests/integration/components/date-time-input-range-test.js index c599f2b407..e9d035f879 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/date-time-input-range-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/date-time-input-range-test.js @@ -1,8 +1,10 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; - -moduleForComponent("date-time-input-range", { integration: true }); +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; function fromDateInput() { return queryAll(".from.d-date-time-input .date-picker")[0]; @@ -22,17 +24,23 @@ function toTimeInput() { const DEFAULT_DATE_TIME = moment("2019-01-29 14:45"); -componentTest("default", { - template: `{{date-time-input-range from=from to=to}}`, +discourseModule("Integration | Component | date-time-input-range", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - this.setProperties({ from: DEFAULT_DATE_TIME, to: null }); - }, + componentTest("default", { + template: `{{date-time-input-range from=from to=to}}`, - test(assert) { - assert.equal(fromDateInput().value, "January 29, 2019"); - assert.equal(fromTimeInput().dataset.name, "14:45"); - assert.equal(toDateInput().value, ""); - assert.equal(toTimeInput().dataset.name, "--:--"); - }, + beforeEach() { + this.setProperties({ from: DEFAULT_DATE_TIME, to: null }); + }, + + test(assert) { + assert.equal(fromDateInput().value, "January 29, 2019"); + assert.equal(fromTimeInput().dataset.name, "14:45"); + assert.equal(toDateInput().value, ""); + assert.equal(toTimeInput().dataset.name, "--:--"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/date-time-input-test.js b/app/assets/javascripts/discourse/tests/integration/components/date-time-input-test.js index e790e4df74..b010643c0b 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/date-time-input-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/date-time-input-test.js @@ -1,11 +1,13 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + exists, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForComponent("date-time-input", { integration: true }); - function dateInput() { return queryAll(".date-picker")[0]; } @@ -26,58 +28,62 @@ async function pika(year, month, day) { const DEFAULT_DATE_TIME = moment("2019-01-29 14:45"); -componentTest("default", { - template: `{{date-time-input date=date}}`, +discourseModule("Integration | Component | date-time-input", function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.setProperties({ date: DEFAULT_DATE_TIME }); - }, + componentTest("default", { + template: `{{date-time-input date=date}}`, - test(assert) { - assert.equal(dateInput().value, "January 29, 2019"); - assert.equal(timeInput().dataset.name, "14:45"); - }, -}); - -componentTest("prevents mutations", { - template: `{{date-time-input date=date}}`, - - beforeEach() { - this.setProperties({ date: DEFAULT_DATE_TIME }); - }, - - async test(assert) { - await click(dateInput()); - await pika(2019, 0, 2); - - assert.ok(this.date.isSame(DEFAULT_DATE_TIME)); - }, -}); - -componentTest("allows mutations through actions", { - template: `{{date-time-input date=date onChange=onChange}}`, - - beforeEach() { - this.setProperties({ date: DEFAULT_DATE_TIME }); - this.set("onChange", setDate); - }, - - async test(assert) { - await click(dateInput()); - await pika(2019, 0, 2); - - assert.ok(this.date.isSame(moment("2019-01-02 14:45"))); - }, -}); - -componentTest("can hide time", { - template: `{{date-time-input date=date showTime=false}}`, - - beforeEach() { - this.setProperties({ date: DEFAULT_DATE_TIME }); - }, - - async test(assert) { - assert.notOk(exists(timeInput())); - }, + beforeEach() { + this.setProperties({ date: DEFAULT_DATE_TIME }); + }, + + test(assert) { + assert.equal(dateInput().value, "January 29, 2019"); + assert.equal(timeInput().dataset.name, "14:45"); + }, + }); + + componentTest("prevents mutations", { + template: `{{date-time-input date=date}}`, + + beforeEach() { + this.setProperties({ date: DEFAULT_DATE_TIME }); + }, + + async test(assert) { + await click(dateInput()); + await pika(2019, 0, 2); + + assert.ok(this.date.isSame(DEFAULT_DATE_TIME)); + }, + }); + + componentTest("allows mutations through actions", { + template: `{{date-time-input date=date onChange=onChange}}`, + + beforeEach() { + this.setProperties({ date: DEFAULT_DATE_TIME }); + this.set("onChange", setDate); + }, + + async test(assert) { + await click(dateInput()); + await pika(2019, 0, 2); + + assert.ok(this.date.isSame(moment("2019-01-02 14:45"))); + }, + }); + + componentTest("can hide time", { + template: `{{date-time-input date=date showTime=false}}`, + + beforeEach() { + this.setProperties({ date: DEFAULT_DATE_TIME }); + }, + + async test(assert) { + assert.notOk(exists(timeInput())); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js b/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js index 3d8255ce9d..4f748269cc 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/highlighted-code-test.js @@ -1,38 +1,44 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; const LONG_CODE_BLOCK = "puts a\n".repeat(15000); -moduleForComponent("highlighted-code", { integration: true }); +discourseModule("Integration | Component | highlighted-code", function (hooks) { + setupRenderingTest(hooks); -componentTest("highlighting code", { - template: "{{highlighted-code lang='ruby' code=code}}", + componentTest("highlighting code", { + template: "{{highlighted-code lang='ruby' code=code}}", - beforeEach() { - this.session.highlightJsPath = - "assets/highlightjs/highlight-test-bundle.min.js"; - this.set("code", "def test; end"); - }, + beforeEach() { + this.session.highlightJsPath = + "assets/highlightjs/highlight-test-bundle.min.js"; + this.set("code", "def test; end"); + }, - test(assert) { - assert.equal( - queryAll("code.ruby.hljs .hljs-function .hljs-keyword").text().trim(), - "def" - ); - }, -}); - -componentTest("large code blocks are not highlighted", { - template: "{{highlighted-code lang='ruby' code=code}}", - - beforeEach() { - this.session.highlightJsPath = - "assets/highlightjs/highlight-test-bundle.min.js"; - this.set("code", LONG_CODE_BLOCK); - }, - - test(assert) { - assert.equal(queryAll("code").text().trim(), LONG_CODE_BLOCK.trim()); - }, + test(assert) { + assert.equal( + queryAll("code.ruby.hljs .hljs-function .hljs-keyword").text().trim(), + "def" + ); + }, + }); + + componentTest("large code blocks are not highlighted", { + template: "{{highlighted-code lang='ruby' code=code}}", + + beforeEach() { + this.session.highlightJsPath = + "assets/highlightjs/highlight-test-bundle.min.js"; + this.set("code", LONG_CODE_BLOCK); + }, + + test(assert) { + assert.equal(queryAll("code").text().trim(), LONG_CODE_BLOCK.trim()); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/html-safe-helper-test.js b/app/assets/javascripts/discourse/tests/integration/components/html-safe-helper-test.js index d7b1442dae..27e71cee90 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/html-safe-helper-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/html-safe-helper-test.js @@ -1,16 +1,20 @@ -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; -moduleForComponent("html-safe-helper", { integration: true }); +import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -componentTest("default", { - template: "{{html-safe string}}", +discourseModule("Integration | Component | html-safe-helper", function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.set("string", "

biscuits

"); - }, + componentTest("default", { + template: "{{html-safe string}}", - async test(assert) { - assert.ok(exists("p.cookies"), "it displays the string as html"); - }, + beforeEach() { + this.set("string", "

biscuits

"); + }, + + async test(assert) { + assert.ok(exists("p.cookies"), "it displays the string as html"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/iframed-html-test.js b/app/assets/javascripts/discourse/tests/integration/components/iframed-html-test.js index c4187b133f..d2ca70ba61 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/iframed-html-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/iframed-html-test.js @@ -1,25 +1,32 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForComponent("iframed-html", { integration: true }); +discourseModule("Integration | Component | iframed-html", function (hooks) { + setupRenderingTest(hooks); -componentTest("appends the html into the iframe", { - template: `{{iframed-html html="

hello

" className='this-is-an-iframe'}}`, + componentTest("appends the html into the iframe", { + template: `{{iframed-html html="

hello

" className='this-is-an-iframe'}}`, - async test(assert) { - const iframe = queryAll("iframe.this-is-an-iframe"); - assert.equal(iframe.length, 1, "inserts an iframe"); + async test(assert) { + const iframe = queryAll("iframe.this-is-an-iframe"); + assert.equal(iframe.length, 1, "inserts an iframe"); - assert.ok( - iframe[0].classList.contains("this-is-an-iframe"), - "Adds className to the iframes classList" - ); + assert.ok( + iframe[0].classList.contains("this-is-an-iframe"), + "Adds className to the iframes classList" + ); - assert.equal( - iframe[0].contentWindow.document.body.querySelectorAll("#find-me").length, - 1, - "inserts the passed in html into the iframe" - ); - }, + assert.equal( + iframe[0].contentWindow.document.body.querySelectorAll("#find-me") + .length, + 1, + "inserts the passed in html into the iframe" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/image-uploader-test.js b/app/assets/javascripts/discourse/tests/integration/components/image-uploader-test.js index b67704f345..d1b5b74b72 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/image-uploader-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/image-uploader-test.js @@ -1,93 +1,99 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForComponent("image-uploader", { integration: true }); +discourseModule("Integration | Component | image-uploader", function (hooks) { + setupRenderingTest(hooks); -componentTest("with image", { - template: - "{{image-uploader imageUrl='/images/avatar.png' placeholderUrl='/not/used.png'}}", + componentTest("with image", { + template: + "{{image-uploader imageUrl='/images/avatar.png' placeholderUrl='/not/used.png'}}", - async test(assert) { - assert.equal( - queryAll(".d-icon-far-image").length, - 1, - "it displays the upload icon" - ); + async test(assert) { + assert.equal( + queryAll(".d-icon-far-image").length, + 1, + "it displays the upload icon" + ); - assert.equal( - queryAll(".d-icon-far-trash-alt").length, - 1, - "it displays the trash icon" - ); + assert.equal( + queryAll(".d-icon-far-trash-alt").length, + 1, + "it displays the trash icon" + ); - assert.equal( - queryAll(".placeholder-overlay").length, - 0, - "it does not display the placeholder image" - ); + assert.equal( + queryAll(".placeholder-overlay").length, + 0, + "it does not display the placeholder image" + ); - await click(".image-uploader-lightbox-btn"); + await click(".image-uploader-lightbox-btn"); - assert.equal( - $(".mfp-container").length, - 1, - "it displays the image lightbox" - ); - }, -}); - -componentTest("without image", { - template: "{{image-uploader}}", - - test(assert) { - assert.equal( - queryAll(".d-icon-far-image").length, - 1, - "it displays the upload icon" - ); - - assert.equal( - queryAll(".d-icon-far-trash-alt").length, - 0, - "it does not display trash icon" - ); - - assert.equal( - queryAll(".image-uploader-lightbox-btn").length, - 0, - "it does not display the button to open image lightbox" - ); - }, -}); - -componentTest("with placeholder", { - template: "{{image-uploader placeholderUrl='/images/avatar.png'}}", - - test(assert) { - assert.equal( - queryAll(".d-icon-far-image").length, - 1, - "it displays the upload icon" - ); - - assert.equal( - queryAll(".d-icon-far-trash-alt").length, - 0, - "it does not display trash icon" - ); - - assert.equal( - queryAll(".image-uploader-lightbox-btn").length, - 0, - "it does not display the button to open image lightbox" - ); - - assert.equal( - queryAll(".placeholder-overlay").length, - 1, - "it displays the placeholder image" - ); - }, + assert.equal( + $(".mfp-container").length, + 1, + "it displays the image lightbox" + ); + }, + }); + + componentTest("without image", { + template: "{{image-uploader}}", + + test(assert) { + assert.equal( + queryAll(".d-icon-far-image").length, + 1, + "it displays the upload icon" + ); + + assert.equal( + queryAll(".d-icon-far-trash-alt").length, + 0, + "it does not display trash icon" + ); + + assert.equal( + queryAll(".image-uploader-lightbox-btn").length, + 0, + "it does not display the button to open image lightbox" + ); + }, + }); + + componentTest("with placeholder", { + template: "{{image-uploader placeholderUrl='/images/avatar.png'}}", + + test(assert) { + assert.equal( + queryAll(".d-icon-far-image").length, + 1, + "it displays the upload icon" + ); + + assert.equal( + queryAll(".d-icon-far-trash-alt").length, + 0, + "it does not display trash icon" + ); + + assert.equal( + queryAll(".image-uploader-lightbox-btn").length, + 0, + "it does not display the button to open image lightbox" + ); + + assert.equal( + queryAll(".placeholder-overlay").length, + 1, + "it displays the placeholder image" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/keyboard-shortcuts-test.js b/app/assets/javascripts/discourse/tests/integration/components/keyboard-shortcuts-test.js deleted file mode 100644 index 4ec83d19ca..0000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/keyboard-shortcuts-test.js +++ /dev/null @@ -1,156 +0,0 @@ -import { test, module } from "qunit"; -import DiscourseURL from "discourse/lib/url"; -import sinon from "sinon"; -import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts"; - -let testMouseTrap; - -module("lib:keyboard-shortcuts", { - beforeEach() { - let _bindings = {}; - - testMouseTrap = { - bind: function (bindings, callback) { - let registerBinding = function (binding) { - _bindings[binding] = callback; - }.bind(this); - - if (Array.isArray(bindings)) { - bindings.forEach(registerBinding, this); - } else { - registerBinding(bindings); - } - }, - - trigger: function (binding) { - _bindings[binding].call(); - }, - }; - - sinon.stub(DiscourseURL, "routeTo"); - - $("#qunit-fixture").html( - [ - "
", - "" + "
", - "
", - "
    ", - "
  • ", - "
  • ", - "
  • ", - "
  • ", - "
", - "
", - "", - " ", - "
", - " ", - "
", - "", - "
", - "", - "
", - "
", - "
", - "
", - "
", - ].join("\n") - ); - }, - - afterEach() { - $("#qunit-scratch").html(""); - testMouseTrap = undefined; - }, -}); - -let pathBindings = KeyboardShortcuts.PATH_BINDINGS || {}; -Object.keys(pathBindings).forEach((path) => { - const binding = pathBindings[path]; - let testName = binding + " goes to " + path; - - test(testName, function (assert) { - KeyboardShortcuts.bindEvents(); - testMouseTrap.trigger(binding); - - assert.ok(DiscourseURL.routeTo.calledWith(path)); - }); -}); - -let clickBindings = KeyboardShortcuts.CLICK_BINDINGS || {}; -Object.keys(clickBindings).forEach((selector) => { - const binding = clickBindings[selector]; - let bindings = binding.split(","); - - let testName = binding + " clicks on " + selector; - - test(testName, function (assert) { - KeyboardShortcuts.bindEvents(); - $(selector).on("click", function () { - assert.ok(true, selector + " was clicked"); - }); - - bindings.forEach(function (b) { - testMouseTrap.trigger(b); - }, this); - }); -}); - -let functionBindings = KeyboardShortcuts.FUNCTION_BINDINGS || {}; -Object.keys(functionBindings).forEach((func) => { - const binding = functionBindings[func]; - let testName = binding + " calls " + func; - - test(testName, function (assert) { - sinon.stub(KeyboardShortcuts, func, function () { - assert.ok(true, func + " is called when " + binding + " is triggered"); - }); - KeyboardShortcuts.bindEvents(); - - testMouseTrap.trigger(binding); - }); -}); - -test("selectDown calls _moveSelection with 1", function (assert) { - let stub = sinon.stub(KeyboardShortcuts, "_moveSelection"); - - KeyboardShortcuts.selectDown(); - assert.ok(stub.calledWith(1), "_moveSelection is called with 1"); -}); - -test("selectUp calls _moveSelection with -1", function (assert) { - let stub = sinon.stub(KeyboardShortcuts, "_moveSelection"); - - KeyboardShortcuts.selectUp(); - assert.ok(stub.calledWith(-1), "_moveSelection is called with -1"); -}); - -test("goBack calls history.back", function (assert) { - let called = false; - sinon.stub(history, "back").callsFake(function () { - called = true; - }); - - KeyboardShortcuts.goBack(); - assert.ok(called, "history.back is called"); -}); - -test("nextSection calls _changeSection with 1", function (assert) { - let spy = sinon.spy(KeyboardShortcuts, "_changeSection"); - - KeyboardShortcuts.nextSection(); - assert.ok(spy.calledWith(1), "_changeSection is called with 1"); -}); - -test("prevSection calls _changeSection with -1", function (assert) { - let spy = sinon.spy(KeyboardShortcuts, "_changeSection"); - - KeyboardShortcuts.prevSection(); - assert.ok(spy.calledWith(-1), "_changeSection is called with -1"); -}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/load-more-test.js b/app/assets/javascripts/discourse/tests/integration/components/load-more-test.js index d5a9310b3d..a9d2138b52 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/load-more-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/load-more-test.js @@ -1,28 +1,32 @@ -import { moduleForComponent } from "ember-qunit"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; import { configureEyeline } from "discourse/lib/eyeline"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForComponent("load-more", { integration: true }); +discourseModule("Integration | Component | load-more", function (hooks) { + setupRenderingTest(hooks); -componentTest("updates once after initialization", { - template: ` - {{#load-more selector=".numbers tr" action=loadMore}} -
- {{/load-more}}`, + componentTest("updates once after initialization", { + template: ` + {{#load-more selector=".numbers tr" action=loadMore}} +
+ {{/load-more}}`, - beforeEach() { - this.set("loadMore", () => this.set("loadedMore", true)); - configureEyeline({ - skipUpdate: false, - rootElement: "#ember-testing", - }); - }, + beforeEach() { + this.set("loadMore", () => this.set("loadedMore", true)); + configureEyeline({ + skipUpdate: false, + rootElement: "#ember-testing", + }); + }, - afterEach() { - configureEyeline(); - }, + afterEach() { + configureEyeline(); + }, - test(assert) { - assert.ok(this.loadedMore); - }, + test(assert) { + assert.ok(this.loadedMore); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/secret-value-list-test.js b/app/assets/javascripts/discourse/tests/integration/components/secret-value-list-test.js index 0934325936..44a80873ae 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/secret-value-list-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/secret-value-list-test.js @@ -1,96 +1,105 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { fillIn, click } from "@ember/test-helpers"; -moduleForComponent("secret-value-list", { integration: true }); +discourseModule("Integration | Component | secret-value-list", function ( + hooks +) { + setupRenderingTest(hooks); -componentTest("adding a value", { - template: "{{secret-value-list values=values}}", + componentTest("adding a value", { + template: "{{secret-value-list values=values}}", - async test(assert) { - this.set("values", "firstKey|FirstValue\nsecondKey|secondValue"); + async test(assert) { + this.set("values", "firstKey|FirstValue\nsecondKey|secondValue"); - await fillIn(".new-value-input.key", "thirdKey"); - await click(".add-value-btn"); + await fillIn(".new-value-input.key", "thirdKey"); + await click(".add-value-btn"); - assert.ok( - queryAll(".values .value").length === 2, - "it doesn't add the value to the list if secret is missing" - ); + assert.ok( + queryAll(".values .value").length === 2, + "it doesn't add the value to the list if secret is missing" + ); - await fillIn(".new-value-input.key", ""); - await fillIn(".new-value-input.secret", "thirdValue"); - await click(".add-value-btn"); + await fillIn(".new-value-input.key", ""); + await fillIn(".new-value-input.secret", "thirdValue"); + await click(".add-value-btn"); - assert.ok( - queryAll(".values .value").length === 2, - "it doesn't add the value to the list if key is missing" - ); + assert.ok( + queryAll(".values .value").length === 2, + "it doesn't add the value to the list if key is missing" + ); - await fillIn(".new-value-input.key", "thirdKey"); - await fillIn(".new-value-input.secret", "thirdValue"); - await click(".add-value-btn"); + await fillIn(".new-value-input.key", "thirdKey"); + await fillIn(".new-value-input.secret", "thirdValue"); + await click(".add-value-btn"); - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); - assert.deepEqual( - this.values, - "firstKey|FirstValue\nsecondKey|secondValue\nthirdKey|thirdValue", - "it adds the value to the list of values" - ); - }, -}); - -componentTest("adding an invalid value", { - template: "{{secret-value-list values=values}}", - - async test(assert) { - await fillIn(".new-value-input.key", "someString"); - await fillIn(".new-value-input.secret", "keyWithAPipe|Hidden"); - await click(".add-value-btn"); - - assert.ok( - queryAll(".values .value").length === 0, - "it doesn't add the value to the list of values" - ); - - assert.deepEqual( - this.values, - undefined, - "it doesn't add the value to the list of values" - ); - - assert.ok( - queryAll(".validation-error") - .html() - .indexOf(I18n.t("admin.site_settings.secret_list.invalid_input")) > -1, - "it shows validation error" - ); - }, -}); - -componentTest("removing a value", { - template: "{{secret-value-list values=values}}", - - async test(assert) { - this.set("values", "firstKey|FirstValue\nsecondKey|secondValue"); - - await click(".values .value[data-index='0'] .remove-value-btn"); - - assert.ok( - queryAll(".values .value").length === 1, - "it removes the value from the list of values" - ); - - assert.equal( - this.values, - "secondKey|secondValue", - "it removes the expected value" - ); - }, + assert.deepEqual( + this.values, + "firstKey|FirstValue\nsecondKey|secondValue\nthirdKey|thirdValue", + "it adds the value to the list of values" + ); + }, + }); + + componentTest("adding an invalid value", { + template: "{{secret-value-list values=values}}", + + async test(assert) { + await fillIn(".new-value-input.key", "someString"); + await fillIn(".new-value-input.secret", "keyWithAPipe|Hidden"); + await click(".add-value-btn"); + + assert.ok( + queryAll(".values .value").length === 0, + "it doesn't add the value to the list of values" + ); + + assert.deepEqual( + this.values, + undefined, + "it doesn't add the value to the list of values" + ); + + assert.ok( + queryAll(".validation-error") + .html() + .indexOf(I18n.t("admin.site_settings.secret_list.invalid_input")) > + -1, + "it shows validation error" + ); + }, + }); + + componentTest("removing a value", { + template: "{{secret-value-list values=values}}", + + async test(assert) { + this.set("values", "firstKey|FirstValue\nsecondKey|secondValue"); + + await click(".values .value[data-index='0'] .remove-value-btn"); + + assert.ok( + queryAll(".values .value").length === 1, + "it removes the value from the list of values" + ); + + assert.equal( + this.values, + "secondKey|secondValue", + "it removes the expected value" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/api-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/api-test.js index dc85b53326..5a4876dfc9 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/api-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/api-test.js @@ -1,116 +1,123 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import selectKit, { - testSelectKitModule, setDefaultState, DEFAULT_CONTENT, } from "discourse/tests/helpers/select-kit-helper"; import { withPluginApi } from "discourse/lib/plugin-api"; import { clearCallbacks } from "select-kit/mixins/plugin-api"; -testSelectKitModule("select-kit:api", { - beforeEach() { +discourseModule("Integration | Component | select-kit:api", function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { this.setProperties({ + // subject: selectKit(); comboBox: selectKit(".combo-box"), singleSelect: selectKit(".single-select:not(.combo-box)"), }); - }, + }); - afterEach() { + hooks.afterEach(function () { clearCallbacks(); - }, -}); + }); -componentTest("modifySelectKit(identifier).appendContent", { - template: ` - {{combo-box value=value content=content onChange=onChange}} - {{single-select value=value content=content onChange=onChange}} - `, + componentTest("modifySelectKit(identifier).appendContent", { + template: ` + {{combo-box value=value content=content onChange=onChange}} + {{single-select value=value content=content onChange=onChange}} + `, - beforeEach() { - setDefaultState(this, null, { content: DEFAULT_CONTENT }); + beforeEach() { + setDefaultState(this, null, { content: DEFAULT_CONTENT }); - withPluginApi("0.8.43", (api) => { - api.modifySelectKit("combo-box").appendContent(() => { - return { - id: "alpaca", - name: "Alpaca", - }; + withPluginApi("0.8.43", (api) => { + api.modifySelectKit("combo-box").appendContent(() => { + return { + id: "alpaca", + name: "Alpaca", + }; + }); + api.modifySelectKit("combo-box").appendContent(() => {}); }); - api.modifySelectKit("combo-box").appendContent(() => {}); - }); - }, + }, - async test(assert) { - await this.comboBox.expand(); + async test(assert) { + await this.comboBox.expand(); - assert.equal(this.comboBox.rows().length, 4); + assert.equal(this.comboBox.rows().length, 4); - const appendedRow = this.comboBox.rowByIndex(3); - assert.ok(appendedRow.exists()); - assert.equal(appendedRow.value(), "alpaca"); + const appendedRow = this.comboBox.rowByIndex(3); + assert.ok(appendedRow.exists()); + assert.equal(appendedRow.value(), "alpaca"); - await this.comboBox.collapse(); + await this.comboBox.collapse(); - assert.notOk(this.singleSelect.rowByValue("alpaca").exists()); - }, -}); + assert.notOk(this.singleSelect.rowByValue("alpaca").exists()); + }, + }); -componentTest("modifySelectKit(identifier).prependContent", { - template: ` - {{combo-box value=value content=content onChange=onChange}} - {{single-select value=value content=content onChange=onChange}} - `, + componentTest("modifySelectKit(identifier).prependContent", { + template: ` + {{combo-box value=value content=content onChange=onChange}} + {{single-select value=value content=content onChange=onChange}} + `, - beforeEach() { - setDefaultState(this, null, { content: DEFAULT_CONTENT }); + beforeEach() { + setDefaultState(this, null, { content: DEFAULT_CONTENT }); - withPluginApi("0.8.43", (api) => { - api.modifySelectKit("combo-box").prependContent(() => { - return { - id: "alpaca", - name: "Alpaca", - }; + withPluginApi("0.8.43", (api) => { + api.modifySelectKit("combo-box").prependContent(() => { + return { + id: "alpaca", + name: "Alpaca", + }; + }); + api.modifySelectKit("combo-box").prependContent(() => {}); }); - api.modifySelectKit("combo-box").prependContent(() => {}); - }); - }, + }, - async test(assert) { - await this.comboBox.expand(); + async test(assert) { + await this.comboBox.expand(); - assert.equal(this.comboBox.rows().length, 4); + assert.equal(this.comboBox.rows().length, 4); - const prependedRow = this.comboBox.rowByIndex(0); - assert.ok(prependedRow.exists()); - assert.equal(prependedRow.value(), "alpaca"); + const prependedRow = this.comboBox.rowByIndex(0); + assert.ok(prependedRow.exists()); + assert.equal(prependedRow.value(), "alpaca"); - await this.comboBox.collapse(); + await this.comboBox.collapse(); - assert.notOk(this.singleSelect.rowByValue("alpaca").exists()); - }, -}); + assert.notOk(this.singleSelect.rowByValue("alpaca").exists()); + }, + }); -componentTest("modifySelectKit(identifier).onChange", { - template: ` -
- {{combo-box value=value content=content onChange=onChange}} - `, + componentTest("modifySelectKit(identifier).onChange", { + template: ` +
+ {{combo-box value=value content=content onChange=onChange}} + `, - beforeEach() { - setDefaultState(this, null, { content: DEFAULT_CONTENT }); + beforeEach() { + setDefaultState(this, null, { content: DEFAULT_CONTENT }); - withPluginApi("0.8.43", (api) => { - api.modifySelectKit("combo-box").onChange((component, value, item) => { - queryAll("#test").text(item.name); + withPluginApi("0.8.43", (api) => { + api.modifySelectKit("combo-box").onChange((component, value, item) => { + queryAll("#test").text(item.name); + }); }); - }); - }, + }, - async test(assert) { - await this.comboBox.expand(); - await this.comboBox.selectRowByIndex(0); + async test(assert) { + await this.comboBox.expand(); + await this.comboBox.selectRowByIndex(0); - assert.equal(queryAll("#test").text(), "foo"); - }, + assert.equal(queryAll("#test").text(), "foo"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js index 2327492ed7..a7ca05875e 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-chooser-test.js @@ -1,9 +1,10 @@ import createStore from "discourse/tests/helpers/create-store"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; - -testSelectKitModule("category-chooser"); +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; function template(options = []) { return ` @@ -16,198 +17,216 @@ function template(options = []) { `; } -componentTest("with value", { - template: template(), +discourseModule( + "Integration | Component | select-kit/category-chooser", + function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.set("value", 2); - }, - - async test(assert) { - assert.equal(this.subject.header().value(), 2); - assert.equal(this.subject.header().label(), "feature"); - }, -}); - -componentTest("with excludeCategoryId", { - template: template(["excludeCategoryId=2"]), - async test(assert) { - await this.subject.expand(); - - assert.notOk(this.subject.rowByValue(2).exists()); - }, -}); - -componentTest("with scopedCategoryId", { - template: template(["scopedCategoryId=2"]), - - async test(assert) { - await this.subject.expand(); - - assert.equal( - this.subject.rowByIndex(0).title(), - "Discussion about features or potential features of Discourse: how they work, why they work, etc." - ); - assert.equal(this.subject.rowByIndex(0).value(), 2); - assert.equal( - this.subject.rowByIndex(1).title(), - "My idea here is to have mini specs for features we would like built but have no bandwidth to build" - ); - assert.equal(this.subject.rowByIndex(1).value(), 26); - assert.equal(this.subject.rows().length, 2, "default content is scoped"); - - await this.subject.fillInFilter("bug"); - - assert.equal( - this.subject.rowByIndex(0).name(), - "bug", - "search finds outside of scope" - ); - }, -}); - -componentTest("with allowUncategorized=null", { - template: template(["allowUncategorized=null"]), - - beforeEach() { - this.siteSettings.allow_uncategorized_topics = false; - }, - - test(assert) { - assert.equal(this.subject.header().value(), null); - assert.equal(this.subject.header().label(), "category…"); - }, -}); - -componentTest("with allowUncategorized=null rootNone=true", { - template: template(["allowUncategorized=null", "none=true"]), - - beforeEach() { - this.siteSettings.allow_uncategorized_topics = false; - }, - - test(assert) { - assert.equal(this.subject.header().value(), null); - assert.equal(this.subject.header().label(), "(no category)"); - }, -}); - -componentTest("with disallowed uncategorized, none", { - template: template(["allowUncategorized=null", "none='test.root'"]), - - beforeEach() { - I18n.translations[I18n.locale].js.test = { root: "root none label" }; - this.siteSettings.allow_uncategorized_topics = false; - }, - - test(assert) { - assert.equal(this.subject.header().value(), null); - assert.equal(this.subject.header().label(), "root none label"); - }, -}); - -componentTest("with allowed uncategorized", { - template: template(["allowUncategorized=true"]), - - beforeEach() { - this.siteSettings.allow_uncategorized_topics = true; - }, - - test(assert) { - assert.equal(this.subject.header().value(), null); - assert.equal(this.subject.header().label(), "uncategorized"); - }, -}); - -componentTest("with allowed uncategorized and none=true", { - template: template(["allowUncategorized=true", "none=true"]), - - beforeEach() { - this.siteSettings.allow_uncategorized_topics = true; - }, - - test(assert) { - assert.equal(this.subject.header().value(), null); - assert.equal(this.subject.header().label(), "(no category)"); - }, -}); - -componentTest("with allowed uncategorized and none", { - template: template(["allowUncategorized=true", "none='test.root'"]), - - beforeEach() { - I18n.translations[I18n.locale].js.test = { root: "root none label" }; - this.siteSettings.allow_uncategorized_topics = true; - }, - - test(assert) { - assert.equal(this.subject.header().value(), null); - assert.equal(this.subject.header().label(), "root none label"); - }, -}); - -componentTest("filter is case insensitive", { - template: template(), - - async test(assert) { - await this.subject.expand(); - await this.subject.fillInFilter("bug"); - - assert.ok(this.subject.rows().length, 1); - assert.equal(this.subject.rowByIndex(0).name(), "bug"); - - await this.subject.emptyFilter(); - await this.subject.fillInFilter("Bug"); - - assert.ok(this.subject.rows().length, 1); - assert.equal(this.subject.rowByIndex(0).name(), "bug"); - }, -}); - -componentTest("filter works with non english characters", { - template: ` - {{category-chooser - value=value - }} - `, - - beforeEach() { - const store = createStore(); - store.createRecord("category", { - id: 1, - name: "chữ Quốc ngữ", + hooks.beforeEach(function () { + this.set("subject", selectKit()); }); - }, - async test(assert) { - await this.subject.expand(); - await this.subject.fillInFilter("hữ"); + componentTest("with value", { + template: template(), - assert.ok(this.subject.rows().length, 1); - assert.equal(this.subject.rowByIndex(0).name(), "chữ Quốc ngữ"); - }, -}); + beforeEach() { + this.set("value", 2); + }, -componentTest("decodes entities in row title", { - template: ` - {{category-chooser - value=value - options=(hash scopedCategoryId=1) - }} - `, - - beforeEach() { - const store = createStore(); - store.createRecord("category", { - id: 1, - name: "cat-with-entities", - description: "baz "bar ‘foo’", + async test(assert) { + assert.equal(this.subject.header().value(), 2); + assert.equal(this.subject.header().label(), "feature"); + }, }); - }, - async test(assert) { - await this.subject.expand(); + componentTest("with excludeCategoryId", { + template: template(["excludeCategoryId=2"]), + async test(assert) { + await this.subject.expand(); - assert.equal(this.subject.rowByIndex(0).el()[0].title, 'baz "bar ‘foo’'); - }, -}); + assert.notOk(this.subject.rowByValue(2).exists()); + }, + }); + + componentTest("with scopedCategoryId", { + template: template(["scopedCategoryId=2"]), + + async test(assert) { + await this.subject.expand(); + + assert.equal( + this.subject.rowByIndex(0).title(), + "Discussion about features or potential features of Discourse: how they work, why they work, etc." + ); + assert.equal(this.subject.rowByIndex(0).value(), 2); + assert.equal( + this.subject.rowByIndex(1).title(), + "My idea here is to have mini specs for features we would like built but have no bandwidth to build" + ); + assert.equal(this.subject.rowByIndex(1).value(), 26); + assert.equal( + this.subject.rows().length, + 2, + "default content is scoped" + ); + + await this.subject.fillInFilter("bug"); + + assert.equal( + this.subject.rowByIndex(0).name(), + "bug", + "search finds outside of scope" + ); + }, + }); + + componentTest("with allowUncategorized=null", { + template: template(["allowUncategorized=null"]), + + beforeEach() { + this.siteSettings.allow_uncategorized_topics = false; + }, + + test(assert) { + assert.equal(this.subject.header().value(), null); + assert.equal(this.subject.header().label(), "category…"); + }, + }); + + componentTest("with allowUncategorized=null rootNone=true", { + template: template(["allowUncategorized=null", "none=true"]), + + beforeEach() { + this.siteSettings.allow_uncategorized_topics = false; + }, + + test(assert) { + assert.equal(this.subject.header().value(), null); + assert.equal(this.subject.header().label(), "(no category)"); + }, + }); + + componentTest("with disallowed uncategorized, none", { + template: template(["allowUncategorized=null", "none='test.root'"]), + + beforeEach() { + I18n.translations[I18n.locale].js.test = { root: "root none label" }; + this.siteSettings.allow_uncategorized_topics = false; + }, + + test(assert) { + assert.equal(this.subject.header().value(), null); + assert.equal(this.subject.header().label(), "root none label"); + }, + }); + + componentTest("with allowed uncategorized", { + template: template(["allowUncategorized=true"]), + + beforeEach() { + this.siteSettings.allow_uncategorized_topics = true; + }, + + test(assert) { + assert.equal(this.subject.header().value(), null); + assert.equal(this.subject.header().label(), "uncategorized"); + }, + }); + + componentTest("with allowed uncategorized and none=true", { + template: template(["allowUncategorized=true", "none=true"]), + + beforeEach() { + this.siteSettings.allow_uncategorized_topics = true; + }, + + test(assert) { + assert.equal(this.subject.header().value(), null); + assert.equal(this.subject.header().label(), "(no category)"); + }, + }); + + componentTest("with allowed uncategorized and none", { + template: template(["allowUncategorized=true", "none='test.root'"]), + + beforeEach() { + I18n.translations[I18n.locale].js.test = { root: "root none label" }; + this.siteSettings.allow_uncategorized_topics = true; + }, + + test(assert) { + assert.equal(this.subject.header().value(), null); + assert.equal(this.subject.header().label(), "root none label"); + }, + }); + + componentTest("filter is case insensitive", { + template: template(), + + async test(assert) { + await this.subject.expand(); + await this.subject.fillInFilter("bug"); + + assert.ok(this.subject.rows().length, 1); + assert.equal(this.subject.rowByIndex(0).name(), "bug"); + + await this.subject.emptyFilter(); + await this.subject.fillInFilter("Bug"); + + assert.ok(this.subject.rows().length, 1); + assert.equal(this.subject.rowByIndex(0).name(), "bug"); + }, + }); + + componentTest("filter works with non english characters", { + template: ` + {{category-chooser + value=value + }} + `, + + beforeEach() { + const store = createStore(); + store.createRecord("category", { + id: 1, + name: "chữ Quốc ngữ", + }); + }, + + async test(assert) { + await this.subject.expand(); + await this.subject.fillInFilter("hữ"); + + assert.ok(this.subject.rows().length, 1); + assert.equal(this.subject.rowByIndex(0).name(), "chữ Quốc ngữ"); + }, + }); + + componentTest("decodes entities in row title", { + template: ` + {{category-chooser + value=value + options=(hash scopedCategoryId=1) + }} + `, + + beforeEach() { + const store = createStore(); + store.createRecord("category", { + id: 1, + name: "cat-with-entities", + description: "baz "bar ‘foo’", + }); + }, + + async test(assert) { + await this.subject.expand(); + + assert.equal( + this.subject.rowByIndex(0).el()[0].title, + 'baz "bar ‘foo’' + ); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-drop-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-drop-test.js index 42de9e3d88..39c13b7f22 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-drop-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/category-drop-test.js @@ -1,17 +1,17 @@ -import { exists } from "discourse/tests/helpers/qunit-helpers"; +import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers"; import I18n from "I18n"; import DiscourseURL from "discourse/lib/url"; import Category from "discourse/models/category"; -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { NO_CATEGORIES_ID, ALL_CATEGORIES_ID, } from "select-kit/components/category-drop"; import { set } from "@ember/object"; import sinon from "sinon"; - -testSelectKitModule("category-drop"); +import selectKit from "discourse/tests/helpers/select-kit-helper"; function initCategories(context) { const categories = context.site.categoriesList; @@ -47,303 +47,313 @@ function template(options = []) { `; } -componentTest("caretUpIcon", { - template: ` - {{category-drop - category=value - categories=content - }} - `, +discourseModule("Integration | Component | select-kit/category-drop", function ( + hooks +) { + setupRenderingTest(hooks); - async test(assert) { - const $header = this.subject.header().el(); + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - assert.ok( - exists($header.find(`.d-icon-caret-right`)), - "it uses the correct default icon" - ); - }, -}); + componentTest("caretUpIcon", { + template: ` + {{category-drop + category=value + categories=content + }} + `, -componentTest("none", { - template: ` - {{category-drop - category=value - categories=content - }} - `, + async test(assert) { + const $header = this.subject.header().el(); - async test(assert) { - const text = this.subject.header().label(); - assert.equal( - text, - I18n.t("category.all").toLowerCase(), - "it uses the noneLabel" - ); - }, -}); + assert.ok( + exists($header.find(`.d-icon-caret-right`)), + "it uses the correct default icon" + ); + }, + }); -componentTest("[not staff - TL0] displayCategoryDescription", { - template: template(), + componentTest("none", { + template: ` + {{category-drop + category=value + categories=content + }} + `, - beforeEach() { - set(this.currentUser, "staff", false); - set(this.currentUser, "trust_level", 0); + async test(assert) { + const text = this.subject.header().label(); + assert.equal( + text, + I18n.t("category.all").toLowerCase(), + "it uses the noneLabel" + ); + }, + }); - initCategories(this); - }, + componentTest("[not staff - TL0] displayCategoryDescription", { + template: template(), - async test(assert) { - await this.subject.expand(); + beforeEach() { + set(this.currentUser, "staff", false); + set(this.currentUser, "trust_level", 0); - const row = this.subject.rowByValue(this.category.id); - assert.ok( - exists(row.el().find(".category-desc")), - "it shows category description for newcomers" - ); - }, -}); + initCategories(this); + }, -componentTest("[not staff - TL1] displayCategoryDescription", { - template: template(), + async test(assert) { + await this.subject.expand(); - beforeEach() { - set(this.currentUser, "moderator", false); - set(this.currentUser, "admin", false); - set(this.currentUser, "trust_level", 1); - initCategories(this); - }, + const row = this.subject.rowByValue(this.category.id); + assert.ok( + exists(row.el().find(".category-desc")), + "it shows category description for newcomers" + ); + }, + }); - async test(assert) { - await this.subject.expand(); + componentTest("[not staff - TL1] displayCategoryDescription", { + template: template(), - const row = this.subject.rowByValue(this.category.id); - assert.ok( - !exists(row.el().find(".category-desc")), - "it doesn't shows category description for TL0+" - ); - }, -}); + beforeEach() { + set(this.currentUser, "moderator", false); + set(this.currentUser, "admin", false); + set(this.currentUser, "trust_level", 1); + initCategories(this); + }, -componentTest("[staff - TL0] displayCategoryDescription", { - template: template(), + async test(assert) { + await this.subject.expand(); - beforeEach() { - set(this.currentUser, "moderator", true); - set(this.currentUser, "trust_level", 0); + const row = this.subject.rowByValue(this.category.id); + assert.ok( + !exists(row.el().find(".category-desc")), + "it doesn't shows category description for TL0+" + ); + }, + }); - initCategories(this); - }, + componentTest("[staff - TL0] displayCategoryDescription", { + template: template(), - async test(assert) { - await this.subject.expand(); + beforeEach() { + set(this.currentUser, "moderator", true); + set(this.currentUser, "trust_level", 0); - const row = this.subject.rowByValue(this.category.id); - assert.ok( - !exists(row.el().find(".category-desc")), - "it doesn't show category description for staff" - ); - }, -}); + initCategories(this); + }, -componentTest("hideParentCategory (default: false)", { - template: template(), + async test(assert) { + await this.subject.expand(); - beforeEach() { - initCategories(this); - }, + const row = this.subject.rowByValue(this.category.id); + assert.ok( + !exists(row.el().find(".category-desc")), + "it doesn't show category description for staff" + ); + }, + }); - async test(assert) { - await this.subject.expand(); + componentTest("hideParentCategory (default: false)", { + template: template(), - const row = this.subject.rowByValue(this.category.id); - assert.equal(row.value(), this.category.id); - assert.equal(this.category.parent_category_id, null); - }, -}); + beforeEach() { + initCategories(this); + }, -componentTest("hideParentCategory (true)", { - template: template(["hideParentCategory=true"]), + async test(assert) { + await this.subject.expand(); - beforeEach() { - initCategoriesWithParentCategory(this); - }, + const row = this.subject.rowByValue(this.category.id); + assert.equal(row.value(), this.category.id); + assert.equal(this.category.parent_category_id, null); + }, + }); - async test(assert) { - await this.subject.expand(); - - const parentRow = this.subject.rowByValue(this.parentCategory.id); - assert.notOk(parentRow.exists(), "the parent row is not showing"); - - const childCategory = this.categories.firstObject; - const childCategoryId = childCategory.id; - const childRow = this.subject.rowByValue(childCategoryId); - assert.ok(childRow.exists(), "the child row is showing"); - - const $categoryStatus = childRow.el().find(".category-status"); - assert.ok($categoryStatus.text().trim().match(/^spec/)); - }, -}); - -componentTest("allow_uncategorized_topics (true)", { - template: template(), - - beforeEach() { - this.siteSettings.allow_uncategorized_topics = true; - initCategories(this); - }, - - async test(assert) { - await this.subject.expand(); - - const uncategorizedCategoryId = this.site.uncategorized_category_id; - const row = this.subject.rowByValue(uncategorizedCategoryId); - assert.ok(row.exists(), "the uncategorized row is showing"); - }, -}); - -componentTest("allow_uncategorized_topics (false)", { - template: template(), - - beforeEach() { - this.siteSettings.allow_uncategorized_topics = false; - initCategories(this); - }, - - async test(assert) { - await this.subject.expand(); - - const uncategorizedCategoryId = this.site.uncategorized_category_id; - const row = this.subject.rowByValue(uncategorizedCategoryId); - assert.notOk(row.exists(), "the uncategorized row is not showing"); - }, -}); - -componentTest("countSubcategories (default: false)", { - template: template(), - - beforeEach() { - initCategories(this); - }, - - async test(assert) { - await this.subject.expand(); - - const category = Category.findById(7); - const row = this.subject.rowByValue(category.id); - const topicCount = row.el().find(".topic-count").text().trim(); - - assert.equal( - topicCount, - "× 481", - "it doesn't include the topic count of subcategories" - ); - }, -}); - -componentTest("countSubcategories (true)", { - template: template(["countSubcategories=true"]), - - beforeEach() { - initCategories(this); - }, - - async test(assert) { - await this.subject.expand(); - - const category = Category.findById(7); - const row = this.subject.rowByValue(category.id); - const topicCount = row.el().find(".topic-count").text().trim(); - - assert.equal( - topicCount, - "× 584", - "it includes the topic count of subcategories" - ); - }, -}); - -componentTest("shortcuts:default", { - template: template(), - - beforeEach() { - initCategories(this); - this.set("category", null); - }, - - async test(assert) { - await this.subject.expand(); - - assert.equal( - this.subject.rowByIndex(0).value(), - this.categories.firstObject.id, - "Shortcuts are not prepended when no category is selected" - ); - }, -}); - -componentTest("shortcuts:category is set", { - template: template(), - - beforeEach() { - initCategories(this); - }, - - async test(assert) { - await this.subject.expand(); - - assert.equal(this.subject.rowByIndex(0).value(), ALL_CATEGORIES_ID); - }, -}); - -componentTest("shortcuts with parentCategory/subCategory=true:default", { - template: template(["subCategory=true"]), - - beforeEach() { - initCategoriesWithParentCategory(this); - }, - - async test(assert) { - await this.subject.expand(); - - assert.equal(this.subject.rowByIndex(0).value(), NO_CATEGORIES_ID); - }, -}); - -componentTest( - "shortcuts with parentCategory/subCategory=true:category is selected", - { - template: template(["subCategory=true"]), + componentTest("hideParentCategory (true)", { + template: template(["hideParentCategory=true"]), beforeEach() { initCategoriesWithParentCategory(this); - this.set("category", this.categories.firstObject); + }, + + async test(assert) { + await this.subject.expand(); + + const parentRow = this.subject.rowByValue(this.parentCategory.id); + assert.notOk(parentRow.exists(), "the parent row is not showing"); + + const childCategory = this.categories.firstObject; + const childCategoryId = childCategory.id; + const childRow = this.subject.rowByValue(childCategoryId); + assert.ok(childRow.exists(), "the child row is showing"); + + const $categoryStatus = childRow.el().find(".category-status"); + assert.ok($categoryStatus.text().trim().match(/^spec/)); + }, + }); + + componentTest("allow_uncategorized_topics (true)", { + template: template(), + + beforeEach() { + this.siteSettings.allow_uncategorized_topics = true; + initCategories(this); + }, + + async test(assert) { + await this.subject.expand(); + + const uncategorizedCategoryId = this.site.uncategorized_category_id; + const row = this.subject.rowByValue(uncategorizedCategoryId); + assert.ok(row.exists(), "the uncategorized row is showing"); + }, + }); + + componentTest("allow_uncategorized_topics (false)", { + template: template(), + + beforeEach() { + this.siteSettings.allow_uncategorized_topics = false; + initCategories(this); + }, + + async test(assert) { + await this.subject.expand(); + + const uncategorizedCategoryId = this.site.uncategorized_category_id; + const row = this.subject.rowByValue(uncategorizedCategoryId); + assert.notOk(row.exists(), "the uncategorized row is not showing"); + }, + }); + + componentTest("countSubcategories (default: false)", { + template: template(), + + beforeEach() { + initCategories(this); + }, + + async test(assert) { + await this.subject.expand(); + + const category = Category.findById(7); + const row = this.subject.rowByValue(category.id); + const topicCount = row.el().find(".topic-count").text().trim(); + + assert.equal( + topicCount, + "× 481", + "it doesn't include the topic count of subcategories" + ); + }, + }); + + componentTest("countSubcategories (true)", { + template: template(["countSubcategories=true"]), + + beforeEach() { + initCategories(this); + }, + + async test(assert) { + await this.subject.expand(); + + const category = Category.findById(7); + const row = this.subject.rowByValue(category.id); + const topicCount = row.el().find(".topic-count").text().trim(); + + assert.equal( + topicCount, + "× 584", + "it includes the topic count of subcategories" + ); + }, + }); + + componentTest("shortcuts:default", { + template: template(), + + beforeEach() { + initCategories(this); + this.set("category", null); + }, + + async test(assert) { + await this.subject.expand(); + + assert.equal( + this.subject.rowByIndex(0).value(), + this.categories.firstObject.id, + "Shortcuts are not prepended when no category is selected" + ); + }, + }); + + componentTest("shortcuts:category is set", { + template: template(), + + beforeEach() { + initCategories(this); }, async test(assert) { await this.subject.expand(); assert.equal(this.subject.rowByIndex(0).value(), ALL_CATEGORIES_ID); - assert.equal(this.subject.rowByIndex(1).value(), NO_CATEGORIES_ID); }, - } -); + }); -componentTest("category url", { - template: template(), + componentTest("shortcuts with parentCategory/subCategory=true:default", { + template: template(["subCategory=true"]), - beforeEach() { - initCategoriesWithParentCategory(this); - sinon.stub(DiscourseURL, "routeTo"); - }, + beforeEach() { + initCategoriesWithParentCategory(this); + }, - async test(assert) { - await this.subject.expand(); - await this.subject.selectRowByValue(26); + async test(assert) { + await this.subject.expand(); - assert.ok( - DiscourseURL.routeTo.calledWith("/c/feature/spec/26"), - "it builds a correct URL" - ); - }, + assert.equal(this.subject.rowByIndex(0).value(), NO_CATEGORIES_ID); + }, + }); + + componentTest( + "shortcuts with parentCategory/subCategory=true:category is selected", + { + template: template(["subCategory=true"]), + + beforeEach() { + initCategoriesWithParentCategory(this); + this.set("category", this.categories.firstObject); + }, + + async test(assert) { + await this.subject.expand(); + + assert.equal(this.subject.rowByIndex(0).value(), ALL_CATEGORIES_ID); + assert.equal(this.subject.rowByIndex(1).value(), NO_CATEGORIES_ID); + }, + } + ); + + componentTest("category url", { + template: template(), + + beforeEach() { + initCategoriesWithParentCategory(this); + sinon.stub(DiscourseURL, "routeTo"); + }, + + async test(assert) { + await this.subject.expand(); + await this.subject.selectRowByValue(26); + + assert.ok( + DiscourseURL.routeTo.calledWith("/c/feature/spec/26"), + "it builds a correct URL" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/combo-box-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/combo-box-test.js index 65109f1f07..37bc4cac73 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/combo-box-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/combo-box-test.js @@ -1,16 +1,10 @@ -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; +import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForComponent("select-kit/combo-box", { - integration: true, - beforeEach() { - this.set("subject", selectKit()); - }, -}); - const DEFAULT_CONTENT = [ { id: 1, name: "foo" }, { id: 2, name: "bar" }, @@ -30,76 +24,86 @@ const setDefaultState = (ctx, options) => { ctx.setProperties(properties); }; -componentTest("options.clearable", { - template: ` - {{combo-box - value=value - content=content - onChange=onChange - options=(hash clearable=clearable) - }} - `, +discourseModule("Integration | Component | select-kit/combo-box", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - setDefaultState(this, { - clearable: true, - onChange: (value) => { - this.set("value", value); - }, - }); - }, + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - async test(assert) { - const $header = this.subject.header(); + componentTest("options.clearable", { + template: ` + {{combo-box + value=value + content=content + onChange=onChange + options=(hash clearable=clearable) + }} + `, - assert.ok( - exists($header.el().find(".btn-clear")), - "it shows the clear button" - ); - assert.equal($header.value(), DEFAULT_VALUE); + beforeEach() { + setDefaultState(this, { + clearable: true, + onChange: (value) => { + this.set("value", value); + }, + }); + }, - await click($header.el().find(".btn-clear")); + async test(assert) { + const $header = this.subject.header(); - assert.notOk( - exists($header.el().find(".btn-clear")), - "it hides the clear button" - ); - assert.equal($header.value(), null); - }, -}); - -componentTest("options.{caretUpIcon,caretDownIcon}", { - template: ` - {{combo-box - value=value - content=content - options=(hash - caretUpIcon=caretUpIcon - caretDownIcon=caretDownIcon - ) - }} - `, - - beforeEach() { - setDefaultState(this, { - caretUpIcon: "pencil-alt", - caretDownIcon: "trash-alt", - }); - }, - - async test(assert) { - const $header = this.subject.header().el(); - - assert.ok( - exists($header.find(`.d-icon-${this.caretDownIcon}`)), - "it uses the icon provided" - ); - - await this.subject.expand(); - - assert.ok( - exists($header.find(`.d-icon-${this.caretUpIcon}`)), - "it uses the icon provided" - ); - }, + assert.ok( + exists($header.el().find(".btn-clear")), + "it shows the clear button" + ); + assert.equal($header.value(), DEFAULT_VALUE); + + await click($header.el().find(".btn-clear")); + + assert.notOk( + exists($header.el().find(".btn-clear")), + "it hides the clear button" + ); + assert.equal($header.value(), null); + }, + }); + + componentTest("options.{caretUpIcon,caretDownIcon}", { + template: ` + {{combo-box + value=value + content=content + options=(hash + caretUpIcon=caretUpIcon + caretDownIcon=caretDownIcon + ) + }} + `, + + beforeEach() { + setDefaultState(this, { + caretUpIcon: "pencil-alt", + caretDownIcon: "trash-alt", + }); + }, + + async test(assert) { + const $header = this.subject.header().el(); + + assert.ok( + exists($header.find(`.d-icon-${this.caretDownIcon}`)), + "it uses the icon provided" + ); + + await this.subject.expand(); + + assert.ok( + exists($header.find(`.d-icon-${this.caretUpIcon}`)), + "it uses the icon provided" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/dropdown-select-box-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/dropdown-select-box-test.js index 6932476f0e..124c31a5c7 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/dropdown-select-box-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/dropdown-select-box-test.js @@ -1,14 +1,8 @@ -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; +import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; - -moduleForComponent("select-kit/dropdown-select-box", { - integration: true, - beforeEach() { - this.set("subject", selectKit()); - }, -}); const DEFAULT_CONTENT = [ { id: 1, name: "foo" }, @@ -32,84 +26,95 @@ const setDefaultState = (ctx, options) => { ctx.setProperties(properties); }; -componentTest("selection behavior", { - template: ` - {{dropdown-select-box - value=value - content=content - }} - `, +discourseModule( + "Integration | Component | select-kit/dropdown-select-box", + function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - setDefaultState(this); - }, - - async test(assert) { - await this.subject.expand(); - assert.ok(this.subject.isExpanded()); - - await this.subject.selectRowByValue(DEFAULT_VALUE); - assert.notOk( - this.subject.isExpanded(), - "it collapses the dropdown on select" - ); - }, -}); - -componentTest("options.showFullTitle=false", { - template: ` - {{dropdown-select-box - value=value - content=content - options=(hash - icon="times" - showFullTitle=showFullTitle - none=none - ) - }} - `, - - beforeEach() { - setDefaultState(this, { - value: null, - showFullTitle: false, - none: "test_none", + hooks.beforeEach(function () { + this.set("subject", selectKit()); }); - }, - async test(assert) { - assert.ok( - !exists(this.subject.header().el().find(".selected-name")), - "it hides the text of the selected item" - ); + componentTest("selection behavior", { + template: ` + {{dropdown-select-box + value=value + content=content + }} + `, - assert.equal( - this.subject.header().el().attr("title"), - "[en_US.test_none]", - "it adds a title attribute to the button" - ); - }, -}); + beforeEach() { + setDefaultState(this); + }, -componentTest("options.showFullTitle=true", { - template: ` - {{dropdown-select-box - value=value - content=content - options=(hash - showFullTitle=showFullTitle - ) - }} - `, + async test(assert) { + await this.subject.expand(); + assert.ok(this.subject.isExpanded()); - beforeEach() { - setDefaultState(this, { showFullTitle: true }); - }, + await this.subject.selectRowByValue(DEFAULT_VALUE); + assert.notOk( + this.subject.isExpanded(), + "it collapses the dropdown on select" + ); + }, + }); - async test(assert) { - assert.ok( - exists(this.subject.header().el().find(".selected-name")), - "it shows the text of the selected item" - ); - }, -}); + componentTest("options.showFullTitle=false", { + template: ` + {{dropdown-select-box + value=value + content=content + options=(hash + icon="times" + showFullTitle=showFullTitle + none=none + ) + }} + `, + + beforeEach() { + setDefaultState(this, { + value: null, + showFullTitle: false, + none: "test_none", + }); + }, + + async test(assert) { + assert.ok( + !exists(this.subject.header().el().find(".selected-name")), + "it hides the text of the selected item" + ); + + assert.equal( + this.subject.header().el().attr("title"), + "[en_US.test_none]", + "it adds a title attribute to the button" + ); + }, + }); + + componentTest("options.showFullTitle=true", { + template: ` + {{dropdown-select-box + value=value + content=content + options=(hash + showFullTitle=showFullTitle + ) + }} + `, + + beforeEach() { + setDefaultState(this, { showFullTitle: true }); + }, + + async test(assert) { + assert.ok( + exists(this.subject.header().el().find(".selected-name")), + "it shows the text of the selected item" + ); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/list-setting-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/list-setting-test.js index a702859684..45a1806847 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/list-setting-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/list-setting-test.js @@ -1,7 +1,8 @@ -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; - -testSelectKitModule("list-setting"); +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; function template(options = []) { return ` @@ -15,21 +16,31 @@ function template(options = []) { `; } -componentTest("default", { - template: template(), +discourseModule("Integration | Component | select-kit/list-setting", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - this.set("value", ["bold", "italic"]); - this.set("choices", ["bold", "italic", "underline"]); - }, + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - async test(assert) { - assert.equal(this.subject.header().name(), "bold,italic"); - assert.equal(this.subject.header().value(), "bold,italic"); + componentTest("default", { + template: template(), - await this.subject.expand(); + beforeEach() { + this.set("value", ["bold", "italic"]); + this.set("choices", ["bold", "italic", "underline"]); + }, - assert.equal(this.subject.rows().length, 1); - assert.equal(this.subject.rowByIndex(0).value(), "underline"); - }, + async test(assert) { + assert.equal(this.subject.header().name(), "bold,italic"); + assert.equal(this.subject.header().value(), "bold,italic"); + + await this.subject.expand(); + + assert.equal(this.subject.rows().length, 1); + assert.equal(this.subject.rowByIndex(0).value(), "underline"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/mini-tag-chooser-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/mini-tag-chooser-test.js index e9b04076b6..bd1431e25b 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/mini-tag-chooser-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/mini-tag-chooser-test.js @@ -1,68 +1,78 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; -testSelectKitModule("mini-tag-chooser"); +discourseModule( + "Integration | Component | select-kit/mini-tag-chooser", + function (hooks) { + setupRenderingTest(hooks); -function template() { - return `{{mini-tag-chooser value=value}}`; -} + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); -componentTest("displays tags", { - template: template(), + componentTest("displays tags", { + template: `{{mini-tag-chooser value=value}}`, - beforeEach() { - this.set("value", ["foo", "bar"]); - }, + beforeEach() { + this.set("value", ["foo", "bar"]); + }, - async test(assert) { - assert.equal(this.subject.header().value(), "foo,bar"); - }, -}); + async test(assert) { + assert.equal(this.subject.header().value(), "foo,bar"); + }, + }); -componentTest("create a tag", { - template: template(), + componentTest("create a tag", { + template: `{{mini-tag-chooser value=value}}`, - beforeEach() { - this.set("value", ["foo", "bar"]); - }, + beforeEach() { + this.set("value", ["foo", "bar"]); + }, - async test(assert) { - assert.equal(this.subject.header().value(), "foo,bar"); + async test(assert) { + assert.equal(this.subject.header().value(), "foo,bar"); - await this.subject.expand(); - await this.subject.fillInFilter("mon"); - assert.equal(queryAll(".select-kit-row").text().trim(), "monkey x1"); - await this.subject.fillInFilter("key"); - assert.equal(queryAll(".select-kit-row").text().trim(), "monkey x1"); - await this.subject.keyboard("enter"); + await this.subject.expand(); + await this.subject.fillInFilter("mon"); + assert.equal(queryAll(".select-kit-row").text().trim(), "monkey x1"); + await this.subject.fillInFilter("key"); + assert.equal(queryAll(".select-kit-row").text().trim(), "monkey x1"); + await this.subject.keyboard("enter"); - assert.equal(this.subject.header().value(), "foo,bar,monkey"); - }, -}); + assert.equal(this.subject.header().value(), "foo,bar,monkey"); + }, + }); -componentTest("max_tags_per_topic", { - template: template(), + componentTest("max_tags_per_topic", { + template: `{{mini-tag-chooser value=value}}`, - beforeEach() { - this.set("value", ["foo", "bar"]); - this.siteSettings.max_tags_per_topic = 2; - }, + beforeEach() { + this.set("value", ["foo", "bar"]); + this.siteSettings.max_tags_per_topic = 2; + }, - async test(assert) { - assert.equal(this.subject.header().value(), "foo,bar"); + async test(assert) { + assert.equal(this.subject.header().value(), "foo,bar"); - await this.subject.expand(); - await this.subject.fillInFilter("baz"); - await this.subject.keyboard("enter"); + await this.subject.expand(); + await this.subject.fillInFilter("baz"); + await this.subject.keyboard("enter"); - const error = queryAll(".select-kit-error").text(); - assert.equal( - error, - I18n.t("select_kit.max_content_reached", { - count: this.siteSettings.max_tags_per_topic, - }) - ); - }, -}); + const error = queryAll(".select-kit-error").text(); + assert.equal( + error, + I18n.t("select_kit.max_content_reached", { + count: this.siteSettings.max_tags_per_topic, + }) + ); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/multi-select-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/multi-select-test.js index 1f97c1fdec..1ac0a366c0 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/multi-select-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/multi-select-test.js @@ -1,7 +1,8 @@ -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; - -testSelectKitModule("multi-select"); +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; function template(options = []) { return ` @@ -32,32 +33,42 @@ const setDefaultState = (ctx, options) => { ctx.setProperties(properties); }; -componentTest("content", { - template: template(), +discourseModule("Integration | Component | select-kit/multi-select", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - setDefaultState(this); - }, + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - async test(assert) { - await this.subject.expand(); + componentTest("content", { + template: template(), - const content = this.subject.displayedContent(); - assert.equal(content.length, 3, "it shows rows"); - assert.equal( - content[0].name, - this.content.firstObject.name, - "it has the correct name" - ); - assert.equal( - content[0].id, - this.content.firstObject.id, - "it has the correct value" - ); - assert.equal( - this.subject.header().value(), - null, - "it doesn't set a value from the content" - ); - }, + beforeEach() { + setDefaultState(this); + }, + + async test(assert) { + await this.subject.expand(); + + const content = this.subject.displayedContent(); + assert.equal(content.length, 3, "it shows rows"); + assert.equal( + content[0].name, + this.content.firstObject.name, + "it has the correct name" + ); + assert.equal( + content[0].id, + this.content.firstObject.id, + "it has the correct value" + ); + assert.equal( + this.subject.header().value(), + null, + "it doesn't set a value from the content" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/notifications-button-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/notifications-button-test.js index 463a75ff38..6783f1edc3 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/notifications-button-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/notifications-button-test.js @@ -1,43 +1,54 @@ -import componentTest from "discourse/tests/helpers/component-test"; -import { - testSelectKitModule, +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit, { setDefaultState, } from "discourse/tests/helpers/select-kit-helper"; -testSelectKitModule("notifications-button"); +discourseModule( + "Integration | Component | select-kit/notifications-button", + function (hooks) { + setupRenderingTest(hooks); -componentTest("default", { - template: ` - {{notifications-button - value=value - options=(hash - i18nPrefix=i18nPrefix - i18nPostfix=i18nPostfix - ) - }} - `, + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - beforeEach() { - this.set("value", 1); + componentTest("default", { + template: ` + {{notifications-button + value=value + options=(hash + i18nPrefix=i18nPrefix + i18nPostfix=i18nPostfix + ) + }} + `, - setDefaultState(this, 1, { i18nPrefix: "pre", i18nPostfix: "post" }); - }, + beforeEach() { + this.set("value", 1); - async test(assert) { - assert.ok(this.subject.header().value()); + setDefaultState(this, 1, { i18nPrefix: "pre", i18nPostfix: "post" }); + }, - assert.ok( - this.subject - .header() - .label() - .includes(`${this.i18nPrefix}.regular${this.i18nPostfix}`), - "it shows the regular choice when value is not set" - ); + async test(assert) { + assert.ok(this.subject.header().value()); - const icon = this.subject.header().icon()[0]; - assert.ok( - icon.classList.contains("d-icon-d-regular"), - "it shows the correct icon" - ); - }, -}); + assert.ok( + this.subject + .header() + .label() + .includes(`${this.i18nPrefix}.regular${this.i18nPostfix}`), + "it shows the regular choice when value is not set" + ); + + const icon = this.subject.header().icon()[0]; + assert.ok( + icon.classList.contains("d-icon-d-regular"), + "it shows the correct icon" + ); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/pinned-options-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/pinned-options-test.js index 32d24a0f6d..561d33661c 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/pinned-options-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/pinned-options-test.js @@ -1,6 +1,8 @@ -import { moduleForComponent } from "ember-qunit"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; import Topic from "discourse/models/topic"; const buildTopic = function (pinned = true) { @@ -12,45 +14,49 @@ const buildTopic = function (pinned = true) { }); }; -moduleForComponent("select-kit/pinned-options", { - integration: true, - beforeEach: function () { - this.set("subject", selectKit()); - }, -}); +discourseModule( + "Integration | Component | select-kit/pinned-options", + function (hooks) { + setupRenderingTest(hooks); -componentTest("unpinning", { - template: "{{pinned-options value=topic.pinned topic=topic}}", + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - beforeEach() { - this.siteSettings.automatically_unpin_topics = false; - this.set("topic", buildTopic()); - }, + componentTest("unpinning", { + template: "{{pinned-options value=topic.pinned topic=topic}}", - async test(assert) { - assert.equal(this.subject.header().name(), "pinned"); + beforeEach() { + this.siteSettings.automatically_unpin_topics = false; + this.set("topic", buildTopic()); + }, - await this.subject.expand(); - await this.subject.selectRowByValue("unpinned"); + async test(assert) { + assert.equal(this.subject.header().name(), "pinned"); - assert.equal(this.subject.header().name(), "unpinned"); - }, -}); + await this.subject.expand(); + await this.subject.selectRowByValue("unpinned"); -componentTest("pinning", { - template: "{{pinned-options value=topic.pinned topic=topic}}", + assert.equal(this.subject.header().name(), "unpinned"); + }, + }); - beforeEach() { - this.siteSettings.automatically_unpin_topics = false; - this.set("topic", buildTopic(false)); - }, + componentTest("pinning", { + template: "{{pinned-options value=topic.pinned topic=topic}}", - async test(assert) { - assert.equal(this.subject.header().name(), "unpinned"); + beforeEach() { + this.siteSettings.automatically_unpin_topics = false; + this.set("topic", buildTopic(false)); + }, - await this.subject.expand(); - await this.subject.selectRowByValue("pinned"); + async test(assert) { + assert.equal(this.subject.header().name(), "unpinned"); - assert.equal(this.subject.header().name(), "pinned"); - }, -}); + await this.subject.expand(); + await this.subject.selectRowByValue("pinned"); + + assert.equal(this.subject.header().name(), "pinned"); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/single-select-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/single-select-test.js index e7473b0c1d..fc6acb6002 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/single-select-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/single-select-test.js @@ -1,8 +1,9 @@ +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; - -testSelectKitModule("single-select"); function template(options = []) { return ` @@ -43,261 +44,273 @@ const setDefaultState = (ctx, options) => { ctx.setProperties(properties); }; -componentTest("content", { - template: "{{single-select content=content}}", +discourseModule("Integration | Component | select-kit/single-select", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - setDefaultState(this); - }, + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - async test(assert) { - await this.subject.expand(); + componentTest("content", { + template: "{{single-select content=content}}", - const content = this.subject.displayedContent(); - assert.equal(content.length, 3, "it shows rows"); - assert.equal( - content[0].name, - this.content.firstObject.name, - "it has the correct name" - ); - assert.equal( - content[0].id, - this.content.firstObject.id, - "it has the correct value" - ); - assert.equal( - this.subject.header().value(), - null, - "it doesn't set a value from the content" - ); - }, -}); - -componentTest("value", { - template: template(), - - beforeEach() { - setDefaultState(this); - }, - - test(assert) { - assert.equal( - this.subject.header().value(this.content), - 1, - "it selects the correct content to display" - ); - }, -}); - -componentTest("options.filterable", { - template: template(["filterable=filterable"]), - - beforeEach() { - setDefaultState(this, { filterable: true }); - }, - - async test(assert) { - await this.subject.expand(); - assert.ok(this.subject.filter().exists(), "it shows the filter"); - - const filter = this.subject.displayedContent()[1].name; - await this.subject.fillInFilter(filter); - assert.equal( - this.subject.displayedContent()[0].name, - filter, - "it filters the list" - ); - }, -}); - -componentTest("options.limitMatches", { - template: template(["limitMatches=limitMatches", "filterable=filterable"]), - - beforeEach() { - setDefaultState(this, { limitMatches: 1, filterable: true }); - }, - - async test(assert) { - await this.subject.expand(); - await this.subject.fillInFilter("ba"); - - assert.equal( - this.subject.displayedContent().length, - 1, - "it returns only 1 result" - ); - }, -}); - -componentTest("valueAttribute (deprecated)", { - template: ` - {{single-select - value=value - content=content - valueAttribute="value" - }} - `, - - beforeEach() { - this.set("value", "normal"); - - const content = [ - { name: "Smallest", value: "smallest" }, - { name: "Smaller", value: "smaller" }, - { name: "Normal", value: "normal" }, - { name: "Larger", value: "larger" }, - { name: "Largest", value: "largest" }, - ]; - this.set("content", content); - }, - - async test(assert) { - await this.subject.expand(); - - assert.equal(this.subject.selectedRow().value(), this.value); - }, -}); - -componentTest("none:string", { - template: template(['none="test.none"']), - - beforeEach() { - I18n.translations[I18n.locale].js.test = { none: "(default)" }; - setDefaultState(this, { value: 1 }); - }, - - async test(assert) { - await this.subject.expand(); - - const noneRow = this.subject.rowByIndex(0); - assert.equal(noneRow.value(), null); - assert.equal(noneRow.name(), I18n.t("test.none")); - }, -}); - -componentTest("none:object", { - template: template(["none=none"]), - - beforeEach() { - setDefaultState(this, { none: { value: null, name: "(default)" } }); - }, - - async test(assert) { - await this.subject.expand(); - - const noneRow = this.subject.rowByIndex(0); - assert.equal(noneRow.value(), null); - assert.equal(noneRow.name(), "(default)"); - }, -}); - -componentTest("content is a basic array", { - template: template(['none="test.none"']), - - beforeEach() { - I18n.translations[I18n.locale].js.test = { none: "(default)" }; - setDefaultState(this, { - nameProperty: null, - valueProperty: null, - value: "foo", - content: ["foo", "bar", "baz"], - }); - }, - - async test(assert) { - await this.subject.expand(); - - const noneRow = this.subject.rowByIndex(0); - assert.equal(noneRow.value(), I18n.t("test.none")); - assert.equal(noneRow.name(), I18n.t("test.none")); - assert.equal(this.value, "foo"); - - await this.subject.selectRowByIndex(0); - - assert.equal(this.value, null); - }, -}); - -componentTest("selected value can be 0", { - template: template(), - - beforeEach() { - setDefaultState(this, { - value: 1, - content: [ - { id: 0, name: "foo" }, - { id: 1, name: "bar" }, - ], - }); - }, - - async test(assert) { - assert.equal(this.subject.header().value(), 1); - - await this.subject.expand(); - await this.subject.selectRowByValue(0); - - assert.equal(this.subject.header().value(), 0); - }, -}); - -componentTest("prevents propagating click event on header", { - template: - "{{#d-button icon='times' action=onClick}}{{single-select options=(hash preventsClickPropagation=true) value=value content=content}}{{/d-button}}", - - beforeEach() { - this.setProperties({ - onClick: () => this.set("value", "foo"), - content: DEFAULT_CONTENT, - value: DEFAULT_VALUE, - }); - }, - - async test(assert) { - assert.equal(this.value, DEFAULT_VALUE); - await this.subject.expand(); - assert.equal(this.value, DEFAULT_VALUE); - }, -}); - -componentTest("labelProperty", { - template: '{{single-select labelProperty="foo" value=value content=content}}', - - beforeEach() { - this.setProperties({ - content: [{ id: 1, name: "john", foo: "JACKSON" }], - value: 1, - }); - }, - - async test(assert) { - assert.equal(this.subject.header().label(), "JACKSON"); - - await this.subject.expand(); - - const row = this.subject.rowByValue(1); - - assert.equal(row.label(), "JACKSON"); - }, -}); - -componentTest("titleProperty", { - template: '{{single-select titleProperty="foo" value=value content=content}}', - - beforeEach() { - this.setProperties({ - content: [{ id: 1, name: "john", foo: "JACKSON" }], - value: 1, - }); - }, - - async test(assert) { - assert.equal(this.subject.header().title(), "JACKSON"); - - await this.subject.expand(); - - const row = this.subject.rowByValue(1); - - assert.equal(row.title(), "JACKSON"); - }, + beforeEach() { + setDefaultState(this); + }, + + async test(assert) { + await this.subject.expand(); + + const content = this.subject.displayedContent(); + assert.equal(content.length, 3, "it shows rows"); + assert.equal( + content[0].name, + this.content.firstObject.name, + "it has the correct name" + ); + assert.equal( + content[0].id, + this.content.firstObject.id, + "it has the correct value" + ); + assert.equal( + this.subject.header().value(), + null, + "it doesn't set a value from the content" + ); + }, + }); + + componentTest("value", { + template: template(), + + beforeEach() { + setDefaultState(this); + }, + + test(assert) { + assert.equal( + this.subject.header().value(this.content), + 1, + "it selects the correct content to display" + ); + }, + }); + + componentTest("options.filterable", { + template: template(["filterable=filterable"]), + + beforeEach() { + setDefaultState(this, { filterable: true }); + }, + + async test(assert) { + await this.subject.expand(); + assert.ok(this.subject.filter().exists(), "it shows the filter"); + + const filter = this.subject.displayedContent()[1].name; + await this.subject.fillInFilter(filter); + assert.equal( + this.subject.displayedContent()[0].name, + filter, + "it filters the list" + ); + }, + }); + + componentTest("options.limitMatches", { + template: template(["limitMatches=limitMatches", "filterable=filterable"]), + + beforeEach() { + setDefaultState(this, { limitMatches: 1, filterable: true }); + }, + + async test(assert) { + await this.subject.expand(); + await this.subject.fillInFilter("ba"); + + assert.equal( + this.subject.displayedContent().length, + 1, + "it returns only 1 result" + ); + }, + }); + + componentTest("valueAttribute (deprecated)", { + template: ` + {{single-select + value=value + content=content + valueAttribute="value" + }} + `, + + beforeEach() { + this.set("value", "normal"); + + const content = [ + { name: "Smallest", value: "smallest" }, + { name: "Smaller", value: "smaller" }, + { name: "Normal", value: "normal" }, + { name: "Larger", value: "larger" }, + { name: "Largest", value: "largest" }, + ]; + this.set("content", content); + }, + + async test(assert) { + await this.subject.expand(); + + assert.equal(this.subject.selectedRow().value(), this.value); + }, + }); + + componentTest("none:string", { + template: template(['none="test.none"']), + + beforeEach() { + I18n.translations[I18n.locale].js.test = { none: "(default)" }; + setDefaultState(this, { value: 1 }); + }, + + async test(assert) { + await this.subject.expand(); + + const noneRow = this.subject.rowByIndex(0); + assert.equal(noneRow.value(), null); + assert.equal(noneRow.name(), I18n.t("test.none")); + }, + }); + + componentTest("none:object", { + template: template(["none=none"]), + + beforeEach() { + setDefaultState(this, { none: { value: null, name: "(default)" } }); + }, + + async test(assert) { + await this.subject.expand(); + + const noneRow = this.subject.rowByIndex(0); + assert.equal(noneRow.value(), null); + assert.equal(noneRow.name(), "(default)"); + }, + }); + + componentTest("content is a basic array", { + template: template(['none="test.none"']), + + beforeEach() { + I18n.translations[I18n.locale].js.test = { none: "(default)" }; + setDefaultState(this, { + nameProperty: null, + valueProperty: null, + value: "foo", + content: ["foo", "bar", "baz"], + }); + }, + + async test(assert) { + await this.subject.expand(); + + const noneRow = this.subject.rowByIndex(0); + assert.equal(noneRow.value(), I18n.t("test.none")); + assert.equal(noneRow.name(), I18n.t("test.none")); + assert.equal(this.value, "foo"); + + await this.subject.selectRowByIndex(0); + + assert.equal(this.value, null); + }, + }); + + componentTest("selected value can be 0", { + template: template(), + + beforeEach() { + setDefaultState(this, { + value: 1, + content: [ + { id: 0, name: "foo" }, + { id: 1, name: "bar" }, + ], + }); + }, + + async test(assert) { + assert.equal(this.subject.header().value(), 1); + + await this.subject.expand(); + await this.subject.selectRowByValue(0); + + assert.equal(this.subject.header().value(), 0); + }, + }); + + componentTest("prevents propagating click event on header", { + template: + "{{#d-button icon='times' action=onClick}}{{single-select options=(hash preventsClickPropagation=true) value=value content=content}}{{/d-button}}", + + beforeEach() { + this.setProperties({ + onClick: () => this.set("value", "foo"), + content: DEFAULT_CONTENT, + value: DEFAULT_VALUE, + }); + }, + + async test(assert) { + assert.equal(this.value, DEFAULT_VALUE); + await this.subject.expand(); + assert.equal(this.value, DEFAULT_VALUE); + }, + }); + + componentTest("labelProperty", { + template: + '{{single-select labelProperty="foo" value=value content=content}}', + + beforeEach() { + this.setProperties({ + content: [{ id: 1, name: "john", foo: "JACKSON" }], + value: 1, + }); + }, + + async test(assert) { + assert.equal(this.subject.header().label(), "JACKSON"); + + await this.subject.expand(); + + const row = this.subject.rowByValue(1); + + assert.equal(row.label(), "JACKSON"); + }, + }); + + componentTest("titleProperty", { + template: + '{{single-select titleProperty="foo" value=value content=content}}', + + beforeEach() { + this.setProperties({ + content: [{ id: 1, name: "john", foo: "JACKSON" }], + value: 1, + }); + }, + + async test(assert) { + assert.equal(this.subject.header().title(), "JACKSON"); + + await this.subject.expand(); + + const row = this.subject.rowByValue(1); + + assert.equal(row.title(), "JACKSON"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/tag-drop-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/tag-drop-test.js index be6223d831..fc4aa2a88f 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/tag-drop-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/tag-drop-test.js @@ -1,32 +1,12 @@ +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; import Site from "discourse/models/site"; import { set } from "@ember/object"; import pretender from "discourse/tests/helpers/create-pretender"; - -testSelectKitModule("tag-drop", { - beforeEach() { - const site = Site.current(); - set(site, "top_tags", ["jeff", "neil", "arpit", "régis"]); - - const response = (object) => { - return [200, { "Content-Type": "application/json" }, object]; - }; - - pretender.get("/tags/filter/search", (params) => { - if (params.queryParams.q === "rég") { - return response({ - results: [{ id: "régis", text: "régis", count: 2, pm_count: 0 }], - }); - } else if (params.queryParams.q === "dav") { - return response({ - results: [{ id: "David", text: "David", count: 2, pm_count: 0 }], - }); - } - }); - }, -}); +import selectKit from "discourse/tests/helpers/select-kit-helper"; function initTags(context) { const categories = context.site.categoriesList; @@ -56,34 +36,63 @@ function template(options = []) { `; } -componentTest("default", { - template: template(["tagId=tagId"]), +discourseModule("Integration | Component | select-kit/tag-drop", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - initTags(this); - }, + hooks.beforeEach(function () { + this.set("subject", selectKit()); - async test(assert) { - await this.subject.expand(); + const site = Site.current(); + set(site, "top_tags", ["jeff", "neil", "arpit", "régis"]); - assert.ok(true); - // const row = this.subject.rowByValue(this.category.id); - // assert.ok( - // exists(row.el().find(".category-desc")), - // "it shows category description for newcomers" - // ); + const response = (object) => { + return [200, { "Content-Type": "application/json" }, object]; + }; - const content = this.subject.displayedContent(); + pretender.get("/tags/filter/search", (params) => { + if (params.queryParams.q === "rég") { + return response({ + results: [{ id: "régis", text: "régis", count: 2, pm_count: 0 }], + }); + } else if (params.queryParams.q === "dav") { + return response({ + results: [{ id: "David", text: "David", count: 2, pm_count: 0 }], + }); + } + }); + }); - assert.equal( - content[0].name, - I18n.t("tagging.selector_no_tags"), - "it has the translated label for no-tags" - ); - assert.equal( - content[1].name, - I18n.t("tagging.selector_all_tags"), - "it has the correct label for all-tags" - ); - }, + componentTest("default", { + template: template(["tagId=tagId"]), + + beforeEach() { + initTags(this); + }, + + async test(assert) { + await this.subject.expand(); + + assert.ok(true); + // const row = this.subject.rowByValue(this.category.id); + // assert.ok( + // exists(row.el().find(".category-desc")), + // "it shows category description for newcomers" + // ); + + const content = this.subject.displayedContent(); + + assert.equal( + content[0].name, + I18n.t("tagging.selector_no_tags"), + "it has the translated label for no-tags" + ); + assert.equal( + content[1].name, + I18n.t("tagging.selector_all_tags"), + "it has the correct label for all-tags" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js index 6656bda882..61ceaf1736 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js @@ -1,7 +1,9 @@ -import { moduleForComponent } from "ember-qunit"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; import I18n from "I18n"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import Topic from "discourse/models/topic"; const buildTopic = function (level, archetype = "regular") { @@ -19,53 +21,56 @@ const buildTopic = function (level, archetype = "regular") { const originalTranslation = I18n.translations.en.js.topic.notifications.tracking_pm.title; -moduleForComponent("select-kit/topic-notifications-button", { - integration: true, +discourseModule( + "Integration | Component | select-kit/topic-notifications-button", + function (hooks) { + setupRenderingTest(hooks); - afterEach() { - I18n.translations.en.js.topic.notifications.tracking_pm.title = originalTranslation; - }, -}); + hooks.afterEach(function () { + I18n.translations.en.js.topic.notifications.tracking_pm.title = originalTranslation; + }); -componentTest("the header has a localized title", { - template: - "{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}", + componentTest("the header has a localized title", { + template: + "{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}", - beforeEach() { - this.set("topic", buildTopic(1)); - }, + beforeEach() { + this.set("topic", buildTopic(1)); + }, - async test(assert) { - assert.equal( - selectKit().header().label(), - "Normal", - "it has the correct label" - ); + async test(assert) { + assert.equal( + selectKit().header().label(), + "Normal", + "it has the correct label" + ); - await this.set("topic", buildTopic(2)); + await this.set("topic", buildTopic(2)); - assert.equal( - selectKit().header().label(), - "Tracking", - "it correctly changes the label" - ); - }, -}); + assert.equal( + selectKit().header().label(), + "Tracking", + "it correctly changes the label" + ); + }, + }); -componentTest("the header has a localized title", { - template: - "{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}", + componentTest("the header has a localized title", { + template: + "{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}", - beforeEach() { - I18n.translations.en.js.topic.notifications.tracking_pm.title = `${originalTranslation} PM`; - this.set("topic", buildTopic(2, "private_message")); - }, + beforeEach() { + I18n.translations.en.js.topic.notifications.tracking_pm.title = `${originalTranslation} PM`; + this.set("topic", buildTopic(2, "private_message")); + }, - test(assert) { - assert.equal( - selectKit().header().label(), - `${originalTranslation} PM`, - "it has the correct label for PMs" - ); - }, -}); + test(assert) { + assert.equal( + selectKit().header().label(), + `${originalTranslation} PM`, + "it has the correct label for PMs" + ); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-options-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-options-test.js index 9a072ebaec..a274b44f3b 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-options-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-options-test.js @@ -1,7 +1,9 @@ -import { moduleForComponent } from "ember-qunit"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; import I18n from "I18n"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import Topic from "discourse/models/topic"; const buildTopic = function (archetype) { @@ -30,65 +32,68 @@ function getTranslations(type = "") { }); } -moduleForComponent("select-kit/topic-notifications-options", { - integration: true, -}); +discourseModule( + "Integration | Component | select-kit/topic-notifications-options", + function (hooks) { + setupRenderingTest(hooks); -componentTest("regular topic notification level descriptions", { - template: - "{{topic-notifications-options value=topic.details.notification_level topic=topic}}", + componentTest("regular topic notification level descriptions", { + template: + "{{topic-notifications-options value=topic.details.notification_level topic=topic}}", - beforeEach() { - this.set("topic", buildTopic("regular")); - }, + beforeEach() { + this.set("topic", buildTopic("regular")); + }, - async test(assert) { - await selectKit().expand(); + async test(assert) { + await selectKit().expand(); - const uiTexts = extractDescs(selectKit().rows()); - const descriptions = getTranslations(); + const uiTexts = extractDescs(selectKit().rows()); + const descriptions = getTranslations(); - assert.equal( - uiTexts.length, - descriptions.length, - "it has the correct copy" - ); - uiTexts.forEach((text, index) => { - assert.equal( - text.trim(), - descriptions[index].trim(), - "it has the correct copy" - ); + assert.equal( + uiTexts.length, + descriptions.length, + "it has the correct copy" + ); + uiTexts.forEach((text, index) => { + assert.equal( + text.trim(), + descriptions[index].trim(), + "it has the correct copy" + ); + }); + }, }); - }, -}); -componentTest("PM topic notification level descriptions", { - template: - "{{topic-notifications-options value=topic.details.notification_level topic=topic}}", + componentTest("PM topic notification level descriptions", { + template: + "{{topic-notifications-options value=topic.details.notification_level topic=topic}}", - beforeEach() { - this.set("topic", buildTopic("private_message")); - }, + beforeEach() { + this.set("topic", buildTopic("private_message")); + }, - async test(assert) { - await selectKit().expand(); + async test(assert) { + await selectKit().expand(); - const uiTexts = extractDescs(selectKit().rows()); - const descriptions = getTranslations("_pm"); + const uiTexts = extractDescs(selectKit().rows()); + const descriptions = getTranslations("_pm"); - assert.equal( - uiTexts.length, - descriptions.length, - "it has the correct copy" - ); + assert.equal( + uiTexts.length, + descriptions.length, + "it has the correct copy" + ); - uiTexts.forEach((text, index) => { - assert.equal( - text.trim(), - descriptions[index].trim(), - "it has the correct copy" - ); + uiTexts.forEach((text, index) => { + assert.equal( + text.trim(), + descriptions[index].trim(), + "it has the correct copy" + ); + }); + }, }); - }, -}); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/user-chooser-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/user-chooser-test.js index c3d006fa3b..f6233da364 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/user-chooser-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/user-chooser-test.js @@ -1,33 +1,40 @@ -import componentTest from "discourse/tests/helpers/component-test"; -import { testSelectKitModule } from "discourse/tests/helpers/select-kit-helper"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; -testSelectKitModule("user-chooser"); +discourseModule("Integration | Component | select-kit/user-chooser", function ( + hooks +) { + setupRenderingTest(hooks); -function template() { - return `{{user-chooser value=value}}`; -} + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); -componentTest("displays usernames", { - template: template(), + componentTest("displays usernames", { + template: `{{user-chooser value=value}}`, - beforeEach() { - this.set("value", ["bob", "martin"]); - }, + beforeEach() { + this.set("value", ["bob", "martin"]); + }, - async test(assert) { - assert.equal(this.subject.header().name(), "bob,martin"); - }, -}); - -componentTest("can remove a username", { - template: template(), - - beforeEach() { - this.set("value", ["bob", "martin"]); - }, - - async test(assert) { - await this.subject.deselectItem("bob"); - assert.equal(this.subject.header().name(), "martin"); - }, + async test(assert) { + assert.equal(this.subject.header().name(), "bob,martin"); + }, + }); + + componentTest("can remove a username", { + template: `{{user-chooser value=value}}`, + + beforeEach() { + this.set("value", ["bob", "martin"]); + }, + + async test(assert) { + await this.subject.deselectItem("bob"); + assert.equal(this.subject.header().name(), "martin"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/simple-list-test.js b/app/assets/javascripts/discourse/tests/integration/components/simple-list-test.js index 75ccc2e122..a9d3236d23 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/simple-list-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/simple-list-test.js @@ -1,90 +1,96 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click, triggerKeyEvent, fillIn } from "@ember/test-helpers"; -moduleForComponent("simple-list", { integration: true }); +discourseModule("Integration | Component | simple-list", function (hooks) { + setupRenderingTest(hooks); -componentTest("adding a value", { - template: "{{simple-list values=values}}", + componentTest("adding a value", { + template: "{{simple-list values=values}}", - beforeEach() { - this.set("values", "vinkas\nosama"); - }, + beforeEach() { + this.set("values", "vinkas\nosama"); + }, - async test(assert) { - assert.ok( - queryAll(".add-value-btn[disabled]").length, - "while loading the + button is disabled" - ); + async test(assert) { + assert.ok( + queryAll(".add-value-btn[disabled]").length, + "while loading the + button is disabled" + ); - await fillIn(".add-value-input", "penar"); - await click(".add-value-btn"); + await fillIn(".add-value-input", "penar"); + await click(".add-value-btn"); - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); - assert.ok( - queryAll(".values .value[data-index='2'] .value-input")[0].value === - "penar", - "it sets the correct value for added item" - ); + assert.ok( + queryAll(".values .value[data-index='2'] .value-input")[0].value === + "penar", + "it sets the correct value for added item" + ); - await fillIn(".add-value-input", "eviltrout"); - await triggerKeyEvent(".add-value-input", "keydown", 13); // enter + await fillIn(".add-value-input", "eviltrout"); + await triggerKeyEvent(".add-value-input", "keydown", 13); // enter - assert.ok( - queryAll(".values .value").length === 4, - "it adds the value when keying Enter" - ); - }, -}); - -componentTest("removing a value", { - template: "{{simple-list values=values}}", - - beforeEach() { - this.set("values", "vinkas\nosama"); - }, - - async test(assert) { - await click(".values .value[data-index='0'] .remove-value-btn"); - - assert.ok( - queryAll(".values .value").length === 1, - "it removes the value from the list of values" - ); - - assert.ok( - queryAll(".values .value[data-index='0'] .value-input")[0].value === - "osama", - "it removes the correct value" - ); - }, -}); - -componentTest("delimiter support", { - template: "{{simple-list values=values inputDelimiter='|'}}", - - beforeEach() { - this.set("values", "vinkas|osama"); - }, - - async test(assert) { - await fillIn(".add-value-input", "eviltrout"); - await click(".add-value-btn"); - - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); - - assert.ok( - queryAll(".values .value[data-index='2'] .value-input")[0].value === - "eviltrout", - "it adds the correct value" - ); - }, + assert.ok( + queryAll(".values .value").length === 4, + "it adds the value when keying Enter" + ); + }, + }); + + componentTest("removing a value", { + template: "{{simple-list values=values}}", + + beforeEach() { + this.set("values", "vinkas\nosama"); + }, + + async test(assert) { + await click(".values .value[data-index='0'] .remove-value-btn"); + + assert.ok( + queryAll(".values .value").length === 1, + "it removes the value from the list of values" + ); + + assert.ok( + queryAll(".values .value[data-index='0'] .value-input")[0].value === + "osama", + "it removes the correct value" + ); + }, + }); + + componentTest("delimiter support", { + template: "{{simple-list values=values inputDelimiter='|'}}", + + beforeEach() { + this.set("values", "vinkas|osama"); + }, + + async test(assert) { + await fillIn(".add-value-input", "eviltrout"); + await click(".add-value-btn"); + + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); + + assert.ok( + queryAll(".values .value[data-index='2'] .value-input")[0].value === + "eviltrout", + "it adds the correct value" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/text-field-test.js b/app/assets/javascripts/discourse/tests/integration/components/text-field-test.js index a8085c1c19..3a5b612c58 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/text-field-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/text-field-test.js @@ -1,91 +1,100 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; import sinon from "sinon"; -import { moduleForComponent } from "ember-qunit"; import I18n from "I18n"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { fillIn } from "@ember/test-helpers"; -moduleForComponent("text-field", { integration: true }); +discourseModule("Integration | Component | text-field", function (hooks) { + setupRenderingTest(hooks); -componentTest("renders correctly with no properties set", { - template: `{{text-field}}`, + componentTest("renders correctly with no properties set", { + template: `{{text-field}}`, - test(assert) { - assert.ok(queryAll("input[type=text]").length); - }, -}); - -componentTest("support a placeholder", { - template: `{{text-field placeholderKey="placeholder.i18n.key"}}`, - - beforeEach() { - sinon.stub(I18n, "t").returnsArg(0); - }, - - test(assert) { - assert.ok(queryAll("input[type=text]").length); - assert.equal(queryAll("input").prop("placeholder"), "placeholder.i18n.key"); - }, -}); - -componentTest("sets the dir attribute to ltr for Hebrew text", { - template: `{{text-field value='זהו שם עברי עם מקום עברי'}}`, - beforeEach() { - this.siteSettings.support_mixed_text_direction = true; - }, - - test(assert) { - assert.equal(queryAll("input").attr("dir"), "rtl"); - }, -}); - -componentTest("sets the dir attribute to ltr for English text", { - template: `{{text-field value='This is a ltr title'}}`, - beforeEach() { - this.siteSettings.support_mixed_text_direction = true; - }, - - test(assert) { - assert.equal(queryAll("input").attr("dir"), "ltr"); - }, -}); - -componentTest("supports onChange", { - template: `{{text-field class="tf-test" value=value onChange=changed}}`, - beforeEach() { - this.called = false; - this.newValue = null; - this.set("value", "hello"); - this.set("changed", (v) => { - this.newValue = v; - this.called = true; - }); - }, - async test(assert) { - await fillIn(".tf-test", "hello"); - assert.ok(!this.called); - await fillIn(".tf-test", "new text"); - assert.ok(this.called); - assert.equal(this.newValue, "new text"); - }, -}); - -componentTest("supports onChangeImmediate", { - template: `{{text-field class="tf-test" value=value onChangeImmediate=changed}}`, - beforeEach() { - this.called = false; - this.newValue = null; - this.set("value", "old"); - this.set("changed", (v) => { - this.newValue = v; - this.called = true; - }); - }, - async test(assert) { - await fillIn(".tf-test", "old"); - assert.ok(!this.called); - await fillIn(".tf-test", "no longer old"); - assert.ok(this.called); - assert.equal(this.newValue, "no longer old"); - }, + test(assert) { + assert.ok(queryAll("input[type=text]").length); + }, + }); + + componentTest("support a placeholder", { + template: `{{text-field placeholderKey="placeholder.i18n.key"}}`, + + beforeEach() { + sinon.stub(I18n, "t").returnsArg(0); + }, + + test(assert) { + assert.ok(queryAll("input[type=text]").length); + assert.equal( + queryAll("input").prop("placeholder"), + "placeholder.i18n.key" + ); + }, + }); + + componentTest("sets the dir attribute to ltr for Hebrew text", { + template: `{{text-field value='זהו שם עברי עם מקום עברי'}}`, + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + }, + + test(assert) { + assert.equal(queryAll("input").attr("dir"), "rtl"); + }, + }); + + componentTest("sets the dir attribute to ltr for English text", { + template: `{{text-field value='This is a ltr title'}}`, + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + }, + + test(assert) { + assert.equal(queryAll("input").attr("dir"), "ltr"); + }, + }); + + componentTest("supports onChange", { + template: `{{text-field class="tf-test" value=value onChange=changed}}`, + beforeEach() { + this.called = false; + this.newValue = null; + this.set("value", "hello"); + this.set("changed", (v) => { + this.newValue = v; + this.called = true; + }); + }, + async test(assert) { + await fillIn(".tf-test", "hello"); + assert.ok(!this.called); + await fillIn(".tf-test", "new text"); + assert.ok(this.called); + assert.equal(this.newValue, "new text"); + }, + }); + + componentTest("supports onChangeImmediate", { + template: `{{text-field class="tf-test" value=value onChangeImmediate=changed}}`, + beforeEach() { + this.called = false; + this.newValue = null; + this.set("value", "old"); + this.set("changed", (v) => { + this.newValue = v; + this.called = true; + }); + }, + async test(assert) { + await fillIn(".tf-test", "old"); + assert.ok(!this.called); + await fillIn(".tf-test", "no longer old"); + assert.ok(this.called); + assert.equal(this.newValue, "no longer old"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/time-input-test.js b/app/assets/javascripts/discourse/tests/integration/components/time-input-test.js index 6773b1b8e8..07f5e896ab 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/time-input-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/time-input-test.js @@ -1,56 +1,58 @@ -import { moduleForComponent } from "ember-qunit"; +import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; - -moduleForComponent("time-input", { - integration: true, - - beforeEach() { - this.set("subject", selectKit()); - }, -}); +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; function setTime(time) { this.setProperties(time); } -componentTest("default", { - template: `{{time-input hours=hours minutes=minutes}}`, +discourseModule("Integration | Component | time-input", function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.setProperties({ hours: "14", minutes: "58" }); - }, + hooks.beforeEach(function () { + this.set("subject", selectKit()); + }); - test(assert) { - assert.equal(this.subject.header().name(), "14:58"); - }, -}); - -componentTest("prevents mutations", { - template: `{{time-input hours=hours minutes=minutes}}`, - - beforeEach() { - this.setProperties({ hours: "14", minutes: "58" }); - }, - - async test(assert) { - await this.subject.expand(); - await this.subject.selectRowByIndex(3); - assert.equal(this.subject.header().name(), "14:58"); - }, -}); - -componentTest("allows mutations through actions", { - template: `{{time-input hours=hours minutes=minutes onChange=onChange}}`, - - beforeEach() { - this.setProperties({ hours: "14", minutes: "58" }); - this.set("onChange", setTime); - }, - - async test(assert) { - await this.subject.expand(); - await this.subject.selectRowByIndex(3); - assert.equal(this.subject.header().name(), "00:45"); - }, + componentTest("default", { + template: `{{time-input hours=hours minutes=minutes}}`, + + beforeEach() { + this.setProperties({ hours: "14", minutes: "58" }); + }, + + test(assert) { + assert.equal(this.subject.header().name(), "14:58"); + }, + }); + + componentTest("prevents mutations", { + template: `{{time-input hours=hours minutes=minutes}}`, + + beforeEach() { + this.setProperties({ hours: "14", minutes: "58" }); + }, + + async test(assert) { + await this.subject.expand(); + await this.subject.selectRowByIndex(3); + assert.equal(this.subject.header().name(), "14:58"); + }, + }); + + componentTest("allows mutations through actions", { + template: `{{time-input hours=hours minutes=minutes onChange=onChange}}`, + + beforeEach() { + this.setProperties({ hours: "14", minutes: "58" }); + this.set("onChange", setTime); + }, + + async test(assert) { + await this.subject.expand(); + await this.subject.selectRowByIndex(3); + assert.equal(this.subject.header().name(), "00:45"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/user-selector-test.js b/app/assets/javascripts/discourse/tests/integration/components/user-selector-test.js index 7e327deb66..35d44053f5 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/user-selector-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/user-selector-test.js @@ -1,8 +1,10 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; -import componentTest from "discourse/tests/helpers/component-test"; - -moduleForComponent("user-selector", { integration: true }); +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; function paste(element, text) { let e = new Event("paste"); @@ -10,47 +12,51 @@ function paste(element, text) { element.dispatchEvent(e); } -componentTest("pasting a list of usernames", { - template: `{{user-selector usernames=usernames class="test-selector"}}`, +discourseModule("Integration | Component | user-selector", function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.set("usernames", "evil,trout"); - }, + componentTest("pasting a list of usernames", { + template: `{{user-selector usernames=usernames class="test-selector"}}`, - test(assert) { - let element = queryAll(".test-selector")[0]; + beforeEach() { + this.set("usernames", "evil,trout"); + }, - assert.equal(this.get("usernames"), "evil,trout"); - paste(element, "zip,zap,zoom"); - assert.equal(this.get("usernames"), "evil,trout,zip,zap,zoom"); - paste(element, "evil,abc,abc,abc"); - assert.equal(this.get("usernames"), "evil,trout,zip,zap,zoom,abc"); + test(assert) { + let element = queryAll(".test-selector")[0]; - this.set("usernames", ""); - paste(element, "names with spaces"); - assert.equal(this.get("usernames"), "names,with,spaces"); + assert.equal(this.get("usernames"), "evil,trout"); + paste(element, "zip,zap,zoom"); + assert.equal(this.get("usernames"), "evil,trout,zip,zap,zoom"); + paste(element, "evil,abc,abc,abc"); + assert.equal(this.get("usernames"), "evil,trout,zip,zap,zoom,abc"); - this.set("usernames", null); - paste(element, "@eviltrout,@codinghorror sam"); - assert.equal(this.get("usernames"), "eviltrout,codinghorror,sam"); + this.set("usernames", ""); + paste(element, "names with spaces"); + assert.equal(this.get("usernames"), "names,with,spaces"); - this.set("usernames", null); - paste(element, "eviltrout\nsam\ncodinghorror"); - assert.equal(this.get("usernames"), "eviltrout,sam,codinghorror"); - }, -}); - -componentTest("excluding usernames", { - template: `{{user-selector usernames=usernames excludedUsernames=excludedUsernames class="test-selector"}}`, - - beforeEach() { - this.set("usernames", "mark"); - this.set("excludedUsernames", ["jeff", "sam", "robin"]); - }, - - test(assert) { - let element = queryAll(".test-selector")[0]; - paste(element, "roman,penar,jeff,robin"); - assert.equal(this.get("usernames"), "mark,roman,penar"); - }, + this.set("usernames", null); + paste(element, "@eviltrout,@codinghorror sam"); + assert.equal(this.get("usernames"), "eviltrout,codinghorror,sam"); + + this.set("usernames", null); + paste(element, "eviltrout\nsam\ncodinghorror"); + assert.equal(this.get("usernames"), "eviltrout,sam,codinghorror"); + }, + }); + + componentTest("excluding usernames", { + template: `{{user-selector usernames=usernames excludedUsernames=excludedUsernames class="test-selector"}}`, + + beforeEach() { + this.set("usernames", "mark"); + this.set("excludedUsernames", ["jeff", "sam", "robin"]); + }, + + test(assert) { + let element = queryAll(".test-selector")[0]; + paste(element, "roman,penar,jeff,robin"); + assert.equal(this.get("usernames"), "mark,roman,penar"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/value-list-test.js b/app/assets/javascripts/discourse/tests/integration/components/value-list-test.js index 6e1809190e..84795c9c62 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/value-list-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/value-list-test.js @@ -1,142 +1,148 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { moduleForComponent } from "ember-qunit"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import componentTest from "discourse/tests/helpers/component-test"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForComponent("value-list", { integration: true }); +discourseModule("Integration | Component | value-list", function (hooks) { + setupRenderingTest(hooks); -componentTest("adding a value", { - template: "{{value-list values=values}}", + componentTest("adding a value", { + template: "{{value-list values=values}}", - skip: true, + skip: true, - beforeEach() { - this.set("values", "vinkas\nosama"); - }, + beforeEach() { + this.set("values", "vinkas\nosama"); + }, - async test(assert) { - await selectKit().expand(); - await selectKit().fillInFilter("eviltrout"); - await selectKit().keyboard("enter"); + async test(assert) { + await selectKit().expand(); + await selectKit().fillInFilter("eviltrout"); + await selectKit().keyboard("enter"); - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); - assert.deepEqual( - this.values, - "vinkas\nosama\neviltrout", - "it adds the value to the list of values" - ); - }, -}); - -componentTest("removing a value", { - template: "{{value-list values=values}}", - - beforeEach() { - this.set("values", "vinkas\nosama"); - }, - - async test(assert) { - await click(".values .value[data-index='0'] .remove-value-btn"); - - assert.ok( - queryAll(".values .value").length === 1, - "it removes the value from the list of values" - ); - - assert.equal(this.values, "osama", "it removes the expected value"); - - await selectKit().expand(); - - assert.ok( - queryAll(".select-kit-collection li.select-kit-row span.name")[0] - .innerText === "vinkas", - "it adds the removed value to choices" - ); - }, -}); - -componentTest("selecting a value", { - template: "{{value-list values=values choices=choices}}", - - beforeEach() { - this.setProperties({ - values: "vinkas\nosama", - choices: ["maja", "michael"], - }); - }, - - async test(assert) { - await selectKit().expand(); - await selectKit().selectRowByValue("maja"); - - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); - - assert.deepEqual( - this.values, - "vinkas\nosama\nmaja", - "it adds the value to the list of values" - ); - }, -}); - -componentTest("array support", { - template: "{{value-list values=values inputType='array'}}", - - beforeEach() { - this.set("values", ["vinkas", "osama"]); - }, - - async test(assert) { - this.set("values", ["vinkas", "osama"]); - - await selectKit().expand(); - await selectKit().fillInFilter("eviltrout"); - await selectKit().keyboard("enter"); - - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); - - assert.deepEqual( - this.values, - ["vinkas", "osama", "eviltrout"], - "it adds the value to the list of values" - ); - }, -}); - -componentTest("delimiter support", { - template: "{{value-list values=values inputDelimiter='|'}}", - - beforeEach() { - this.set("values", "vinkas|osama"); - }, - - skip: true, - - async test(assert) { - await selectKit().expand(); - await selectKit().fillInFilter("eviltrout"); - await selectKit().keyboard("enter"); - - assert.ok( - queryAll(".values .value").length === 3, - "it adds the value to the list of values" - ); - - assert.deepEqual( - this.values, - "vinkas|osama|eviltrout", - "it adds the value to the list of values" - ); - }, + assert.deepEqual( + this.values, + "vinkas\nosama\neviltrout", + "it adds the value to the list of values" + ); + }, + }); + + componentTest("removing a value", { + template: "{{value-list values=values}}", + + beforeEach() { + this.set("values", "vinkas\nosama"); + }, + + async test(assert) { + await click(".values .value[data-index='0'] .remove-value-btn"); + + assert.ok( + queryAll(".values .value").length === 1, + "it removes the value from the list of values" + ); + + assert.equal(this.values, "osama", "it removes the expected value"); + + await selectKit().expand(); + + assert.ok( + queryAll(".select-kit-collection li.select-kit-row span.name")[0] + .innerText === "vinkas", + "it adds the removed value to choices" + ); + }, + }); + + componentTest("selecting a value", { + template: "{{value-list values=values choices=choices}}", + + beforeEach() { + this.setProperties({ + values: "vinkas\nosama", + choices: ["maja", "michael"], + }); + }, + + async test(assert) { + await selectKit().expand(); + await selectKit().selectRowByValue("maja"); + + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); + + assert.deepEqual( + this.values, + "vinkas\nosama\nmaja", + "it adds the value to the list of values" + ); + }, + }); + + componentTest("array support", { + template: "{{value-list values=values inputType='array'}}", + + beforeEach() { + this.set("values", ["vinkas", "osama"]); + }, + + async test(assert) { + this.set("values", ["vinkas", "osama"]); + + await selectKit().expand(); + await selectKit().fillInFilter("eviltrout"); + await selectKit().keyboard("enter"); + + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); + + assert.deepEqual( + this.values, + ["vinkas", "osama", "eviltrout"], + "it adds the value to the list of values" + ); + }, + }); + + componentTest("delimiter support", { + template: "{{value-list values=values inputDelimiter='|'}}", + + beforeEach() { + this.set("values", "vinkas|osama"); + }, + + skip: true, + + async test(assert) { + await selectKit().expand(); + await selectKit().fillInFilter("eviltrout"); + await selectKit().keyboard("enter"); + + assert.ok( + queryAll(".values .value").length === 3, + "it adds the value to the list of values" + ); + + assert.deepEqual( + this.values, + "vinkas|osama|eviltrout", + "it adds the value to the list of values" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/actions-summary-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/actions-summary-test.js index 90b51d3376..e717e6a3d4 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/actions-summary-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/actions-summary-test.js @@ -1,28 +1,34 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForWidget("actions-summary"); +discourseModule("Integration | Component | Widget | actions-summary", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("post deleted", { - template: '{{mount-widget widget="actions-summary" args=args}}', - beforeEach() { - this.set("args", { - deleted_at: "2016-01-01", - deletedByUsername: "eviltrout", - deletedByAvatarTemplate: "/images/avatar.png", - }); - }, - test(assert) { - assert.ok( - queryAll(".post-action .d-icon-far-trash-alt").length === 1, - "it has the deleted icon" - ); - assert.ok( - queryAll(".avatar[title=eviltrout]").length === 1, - "it has the deleted by avatar" - ); - }, + componentTest("post deleted", { + template: '{{mount-widget widget="actions-summary" args=args}}', + beforeEach() { + this.set("args", { + deleted_at: "2016-01-01", + deletedByUsername: "eviltrout", + deletedByAvatarTemplate: "/images/avatar.png", + }); + }, + test(assert) { + assert.ok( + queryAll(".post-action .d-icon-far-trash-alt").length === 1, + "it has the deleted icon" + ); + assert.ok( + queryAll(".avatar[title=eviltrout]").length === 1, + "it has the deleted by avatar" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/avatar-flair-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/avatar-flair-test.js index 7f06cd0c9f..3a5c899ca6 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/avatar-flair-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/avatar-flair-test.js @@ -1,40 +1,46 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForWidget("avatar-flair"); +discourseModule("Integration | Component | Widget | avatar-flair", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("avatar flair with an icon", { - template: '{{mount-widget widget="avatar-flair" args=args}}', - beforeEach() { - this.set("args", { - primary_group_flair_url: "fa-bars", - primary_group_flair_bg_color: "CC0000", - primary_group_flair_color: "FFFFFF", - }); - }, - test(assert) { - assert.ok(queryAll(".avatar-flair").length, "it has the tag"); - assert.ok(queryAll("svg.d-icon-bars").length, "it has the svg icon"); - assert.equal( - queryAll(".avatar-flair").attr("style"), - "background-color: #CC0000; color: #FFFFFF; ", - "it has styles" - ); - }, -}); - -widgetTest("avatar flair with an image", { - template: '{{mount-widget widget="avatar-flair" args=args}}', - beforeEach() { - this.set("args", { - primary_group_flair_url: "/images/avatar.png", - }); - }, - test(assert) { - assert.ok(queryAll(".avatar-flair").length, "it has the tag"); - assert.ok(queryAll("svg").length === 0, "it does not have an svg icon"); - }, + componentTest("avatar flair with an icon", { + template: '{{mount-widget widget="avatar-flair" args=args}}', + beforeEach() { + this.set("args", { + primary_group_flair_url: "fa-bars", + primary_group_flair_bg_color: "CC0000", + primary_group_flair_color: "FFFFFF", + }); + }, + test(assert) { + assert.ok(queryAll(".avatar-flair").length, "it has the tag"); + assert.ok(queryAll("svg.d-icon-bars").length, "it has the svg icon"); + assert.equal( + queryAll(".avatar-flair").attr("style"), + "background-color: #CC0000; color: #FFFFFF; ", + "it has styles" + ); + }, + }); + + componentTest("avatar flair with an image", { + template: '{{mount-widget widget="avatar-flair" args=args}}', + beforeEach() { + this.set("args", { + primary_group_flair_url: "/images/avatar.png", + }); + }, + test(assert) { + assert.ok(queryAll(".avatar-flair").length, "it has the tag"); + assert.ok(queryAll("svg").length === 0, "it does not have an svg icon"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/button-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/button-test.js index b96578c88f..196c02b0d6 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/button-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/button-test.js @@ -1,62 +1,72 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForWidget("button"); +discourseModule("Integration | Component | Widget | button", function (hooks) { + setupRenderingTest(hooks); -widgetTest("icon only button", { - template: '{{mount-widget widget="button" args=args}}', + componentTest("icon only button", { + template: '{{mount-widget widget="button" args=args}}', - beforeEach() { - this.set("args", { icon: "far-smile" }); - }, + beforeEach() { + this.set("args", { icon: "far-smile" }); + }, - test(assert) { - assert.ok( - queryAll("button.btn.btn-icon.no-text").length, - "it has all the classes" - ); - assert.ok( - queryAll("button .d-icon.d-icon-far-smile").length, - "it has the icon" - ); - }, -}); - -widgetTest("icon and text button", { - template: '{{mount-widget widget="button" args=args}}', - - beforeEach() { - this.set("args", { icon: "plus", label: "topic.create" }); - }, - - test(assert) { - assert.ok( - queryAll("button.btn.btn-icon-text").length, - "it has all the classes" - ); - assert.ok(queryAll("button .d-icon.d-icon-plus").length, "it has the icon"); - assert.ok( - queryAll("button span.d-button-label").length, - "it has the label" - ); - }, -}); - -widgetTest("text only button", { - template: '{{mount-widget widget="button" args=args}}', - - beforeEach() { - this.set("args", { label: "topic.create" }); - }, - - test(assert) { - assert.ok(queryAll("button.btn.btn-text").length, "it has all the classes"); - assert.ok( - queryAll("button span.d-button-label").length, - "it has the label" - ); - }, + test(assert) { + assert.ok( + queryAll("button.btn.btn-icon.no-text").length, + "it has all the classes" + ); + assert.ok( + queryAll("button .d-icon.d-icon-far-smile").length, + "it has the icon" + ); + }, + }); + + componentTest("icon and text button", { + template: '{{mount-widget widget="button" args=args}}', + + beforeEach() { + this.set("args", { icon: "plus", label: "topic.create" }); + }, + + test(assert) { + assert.ok( + queryAll("button.btn.btn-icon-text").length, + "it has all the classes" + ); + assert.ok( + queryAll("button .d-icon.d-icon-plus").length, + "it has the icon" + ); + assert.ok( + queryAll("button span.d-button-label").length, + "it has the label" + ); + }, + }); + + componentTest("text only button", { + template: '{{mount-widget widget="button" args=args}}', + + beforeEach() { + this.set("args", { label: "topic.create" }); + }, + + test(assert) { + assert.ok( + queryAll("button.btn.btn-text").length, + "it has all the classes" + ); + assert.ok( + queryAll("button span.d-button-label").length, + "it has the label" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/default-notification-item-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/default-notification-item-test.js index 149eafbc46..ddf2365ac5 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/default-notification-item-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/default-notification-item-test.js @@ -1,64 +1,75 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import EmberObject from "@ember/object"; import pretender from "discourse/tests/helpers/create-pretender"; -import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; -moduleForWidget("default-notification-item"); +discourseModule( + "Integration | Component | Widget | default-notification-item", + function (hooks) { + setupRenderingTest(hooks); -widgetTest("sets notification as read on middle click", { - template: '{{mount-widget widget="default-notification-item" args=args}}', - beforeEach() { - this.set( - "args", - EmberObject.create({ - id: 3, - user_id: 1, - notification_type: 6, - read: false, - created_at: "2020-01-01T12:00:00.000Z", - post_number: 1, - topic_id: 10, - fancy_title: "Greetings!", - slug: "greetings", - data: { - topic_title: "Greetings!", - original_post_id: 14, - original_post_type: 1, - original_username: "discobot", - revision_number: null, - display_username: "discobot", - }, - }) - ); - }, - async test(assert) { - let requests = 0; - pretender.put("/notifications/mark-read", (request) => { - ++requests; + componentTest("sets notification as read on middle click", { + template: '{{mount-widget widget="default-notification-item" args=args}}', + beforeEach() { + this.set( + "args", + EmberObject.create({ + id: 3, + user_id: 1, + notification_type: 6, + read: false, + created_at: "2020-01-01T12:00:00.000Z", + post_number: 1, + topic_id: 10, + fancy_title: "Greetings!", + slug: "greetings", + data: { + topic_title: "Greetings!", + original_post_id: 14, + original_post_type: 1, + original_username: "discobot", + revision_number: null, + display_username: "discobot", + }, + }) + ); + }, + async test(assert) { + let requests = 0; + pretender.put("/notifications/mark-read", (request) => { + ++requests; - assert.equal( - request.requestBody, - `id=${this.args.id}`, - "it sets correct request parameters" - ); + assert.equal( + request.requestBody, + `id=${this.args.id}`, + "it sets correct request parameters" + ); - return [200, { "Content-Type": "application/json" }, { success: true }]; + return [ + 200, + { "Content-Type": "application/json" }, + { success: true }, + ]; + }); + + assert.equal(queryAll("li.read").length, 0); + + await $(document).trigger( + $.Event("mouseup", { + target: queryAll("li")[0], + button: 1, + which: 2, + }) + ); + + assert.equal(queryAll("li.read").length, 1); + assert.equal(requests, 1); + }, }); - - assert.equal(queryAll("li.read").length, 0); - - await $(document).trigger( - $.Event("mouseup", { - target: queryAll("li")[0], - button: 1, - which: 2, - }) - ); - - assert.equal(queryAll("li.read").length, 1); - assert.equal(requests, 1); - }, -}); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/hamburger-menu-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/hamburger-menu-test.js index 2d67bb55fc..74fa5b7c08 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/hamburger-menu-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/hamburger-menu-test.js @@ -1,257 +1,263 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { NotificationLevels } from "discourse/lib/notification-levels"; -moduleForWidget("hamburger-menu"); - const topCategoryIds = [2, 3, 1]; let mutedCategoryIds = []; let unreadCategoryIds = []; let categoriesByCount = []; -widgetTest("prioritize faq", { - template: '{{mount-widget widget="hamburger-menu"}}', +discourseModule("Integration | Component | Widget | hamburger-menu", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - this.siteSettings.faq_url = "http://example.com/faq"; - this.currentUser.set("read_faq", false); - }, + componentTest("prioritize faq", { + template: '{{mount-widget widget="hamburger-menu"}}', - test(assert) { - assert.ok(queryAll(".faq-priority").length); - assert.ok(!queryAll(".faq-link").length); - }, -}); + beforeEach() { + this.siteSettings.faq_url = "http://example.com/faq"; + this.currentUser.set("read_faq", false); + }, -widgetTest("prioritize faq - user has read", { - template: '{{mount-widget widget="hamburger-menu"}}', + test(assert) { + assert.ok(queryAll(".faq-priority").length); + assert.ok(!queryAll(".faq-link").length); + }, + }); - beforeEach() { - this.siteSettings.faq_url = "http://example.com/faq"; - this.currentUser.set("read_faq", true); - }, + componentTest("prioritize faq - user has read", { + template: '{{mount-widget widget="hamburger-menu"}}', - test(assert) { - assert.ok(!queryAll(".faq-priority").length); - assert.ok(queryAll(".faq-link").length); - }, -}); + beforeEach() { + this.siteSettings.faq_url = "http://example.com/faq"; + this.currentUser.set("read_faq", true); + }, -widgetTest("staff menu - not staff", { - template: '{{mount-widget widget="hamburger-menu"}}', + test(assert) { + assert.ok(!queryAll(".faq-priority").length); + assert.ok(queryAll(".faq-link").length); + }, + }); - beforeEach() { - this.currentUser.set("staff", false); - }, + componentTest("staff menu - not staff", { + template: '{{mount-widget widget="hamburger-menu"}}', - test(assert) { - assert.ok(!queryAll(".admin-link").length); - }, -}); + beforeEach() { + this.currentUser.set("staff", false); + }, -widgetTest("staff menu - moderator", { - template: '{{mount-widget widget="hamburger-menu"}}', + test(assert) { + assert.ok(!queryAll(".admin-link").length); + }, + }); - beforeEach() { - this.currentUser.set("moderator", true); - }, + componentTest("staff menu - moderator", { + template: '{{mount-widget widget="hamburger-menu"}}', - test(assert) { - assert.ok(queryAll(".admin-link").length); - assert.ok(queryAll(".review").length); - assert.ok(!queryAll(".settings-link").length); - }, -}); + beforeEach() { + this.currentUser.set("moderator", true); + }, -widgetTest("staff menu - admin", { - template: '{{mount-widget widget="hamburger-menu"}}', + test(assert) { + assert.ok(queryAll(".admin-link").length); + assert.ok(queryAll(".review").length); + assert.ok(!queryAll(".settings-link").length); + }, + }); - beforeEach() { - this.currentUser.setProperties({ admin: true }); - }, + componentTest("staff menu - admin", { + template: '{{mount-widget widget="hamburger-menu"}}', - test(assert) { - assert.ok(queryAll(".settings-link").length); - }, -}); + beforeEach() { + this.currentUser.setProperties({ admin: true }); + }, -widgetTest("logged in links", { - template: '{{mount-widget widget="hamburger-menu"}}', + test(assert) { + assert.ok(queryAll(".settings-link").length); + }, + }); - test(assert) { - assert.ok(queryAll(".new-topics-link").length); - assert.ok(queryAll(".unread-topics-link").length); - }, -}); + componentTest("logged in links", { + template: '{{mount-widget widget="hamburger-menu"}}', -widgetTest("general links", { - template: '{{mount-widget widget="hamburger-menu"}}', - anonymous: true, + test(assert) { + assert.ok(queryAll(".new-topics-link").length); + assert.ok(queryAll(".unread-topics-link").length); + }, + }); - test(assert) { - assert.ok(queryAll("li[class='']").length === 0); - assert.ok(queryAll(".latest-topics-link").length); - assert.ok(!queryAll(".new-topics-link").length); - assert.ok(!queryAll(".unread-topics-link").length); - assert.ok(queryAll(".top-topics-link").length); - assert.ok(queryAll(".badge-link").length); - assert.ok(queryAll(".category-link").length > 0); - }, -}); + componentTest("general links", { + template: '{{mount-widget widget="hamburger-menu"}}', + anonymous: true, -let maxCategoriesToDisplay; + test(assert) { + assert.ok(queryAll("li[class='']").length === 0); + assert.ok(queryAll(".latest-topics-link").length); + assert.ok(!queryAll(".new-topics-link").length); + assert.ok(!queryAll(".unread-topics-link").length); + assert.ok(queryAll(".top-topics-link").length); + assert.ok(queryAll(".badge-link").length); + assert.ok(queryAll(".category-link").length > 0); + }, + }); -widgetTest("top categories - anonymous", { - template: '{{mount-widget widget="hamburger-menu"}}', - anonymous: true, + let maxCategoriesToDisplay; - beforeEach() { - this.siteSettings.header_dropdown_category_count = 8; - }, + componentTest("top categories - anonymous", { + template: '{{mount-widget widget="hamburger-menu"}}', + anonymous: true, - test(assert) { - assert.equal(queryAll(".category-link").length, 8); - assert.equal( - queryAll(".category-link .category-name").text(), - this.site - .get("categoriesByCount") - .slice(0, 8) - .map((c) => c.name) - .join("") - ); - }, -}); + beforeEach() { + this.siteSettings.header_dropdown_category_count = 8; + }, -widgetTest("top categories - allow_uncategorized_topics", { - template: '{{mount-widget widget="hamburger-menu"}}', - anonymous: true, + test(assert) { + assert.equal(queryAll(".category-link").length, 8); + assert.equal( + queryAll(".category-link .category-name").text(), + this.site + .get("categoriesByCount") + .slice(0, 8) + .map((c) => c.name) + .join("") + ); + }, + }); - beforeEach() { - this.siteSettings.allow_uncategorized_topics = false; - this.siteSettings.header_dropdown_category_count = 8; - }, + componentTest("top categories - allow_uncategorized_topics", { + template: '{{mount-widget widget="hamburger-menu"}}', + anonymous: true, - test(assert) { - assert.equal(queryAll(".category-link").length, 8); - assert.equal( - queryAll(".category-link .category-name").text(), - this.site - .get("categoriesByCount") - .filter((c) => c.name !== "uncategorized") - .slice(0, 8) - .map((c) => c.name) - .join("") - ); - }, -}); + beforeEach() { + this.siteSettings.allow_uncategorized_topics = false; + this.siteSettings.header_dropdown_category_count = 8; + }, -widgetTest("top categories", { - template: '{{mount-widget widget="hamburger-menu"}}', + test(assert) { + assert.equal(queryAll(".category-link").length, 8); + assert.equal( + queryAll(".category-link .category-name").text(), + this.site + .get("categoriesByCount") + .filter((c) => c.name !== "uncategorized") + .slice(0, 8) + .map((c) => c.name) + .join("") + ); + }, + }); - beforeEach() { - this.siteSettings.header_dropdown_category_count = 8; - maxCategoriesToDisplay = this.siteSettings.header_dropdown_category_count; - categoriesByCount = this.site.get("categoriesByCount").slice(); - categoriesByCount.every((c) => { - if (!topCategoryIds.includes(c.id)) { - if (mutedCategoryIds.length === 0) { - mutedCategoryIds.push(c.id); - c.set("notification_level", NotificationLevels.MUTED); - } else if (unreadCategoryIds.length === 0) { - unreadCategoryIds.push(c.id); - for (let i = 0; i < 5; i++) { - c.topicTrackingState.states["t123" + i] = { - category_id: c.id, - last_read_post_number: 1, - highest_post_number: 2, - notification_level: NotificationLevels.TRACKING, - }; + componentTest("top categories", { + template: '{{mount-widget widget="hamburger-menu"}}', + + beforeEach() { + this.siteSettings.header_dropdown_category_count = 8; + maxCategoriesToDisplay = this.siteSettings.header_dropdown_category_count; + categoriesByCount = this.site.get("categoriesByCount").slice(); + categoriesByCount.every((c) => { + if (!topCategoryIds.includes(c.id)) { + if (mutedCategoryIds.length === 0) { + mutedCategoryIds.push(c.id); + c.set("notification_level", NotificationLevels.MUTED); + } else if (unreadCategoryIds.length === 0) { + unreadCategoryIds.push(c.id); + for (let i = 0; i < 5; i++) { + c.topicTrackingState.states["t123" + i] = { + category_id: c.id, + last_read_post_number: 1, + highest_post_number: 2, + notification_level: NotificationLevels.TRACKING, + }; + } + } else { + unreadCategoryIds.splice(0, 0, c.id); + for (let i = 0; i < 10; i++) { + c.topicTrackingState.states["t321" + i] = { + category_id: c.id, + last_read_post_number: null, + }; + } + return false; } - } else { - unreadCategoryIds.splice(0, 0, c.id); - for (let i = 0; i < 10; i++) { - c.topicTrackingState.states["t321" + i] = { - category_id: c.id, - last_read_post_number: null, - }; - } - return false; } - } - return true; - }); - this.currentUser.set("top_category_ids", topCategoryIds); - }, + return true; + }); + this.currentUser.set("top_category_ids", topCategoryIds); + }, - test(assert) { - assert.equal(queryAll(".category-link").length, maxCategoriesToDisplay); + test(assert) { + assert.equal(queryAll(".category-link").length, maxCategoriesToDisplay); - categoriesByCount = categoriesByCount.filter( - (c) => !mutedCategoryIds.includes(c.id) - ); - let ids = [ - ...unreadCategoryIds, - ...topCategoryIds, - ...categoriesByCount.map((c) => c.id), - ] - .uniq() - .slice(0, maxCategoriesToDisplay); + categoriesByCount = categoriesByCount.filter( + (c) => !mutedCategoryIds.includes(c.id) + ); + let ids = [ + ...unreadCategoryIds, + ...topCategoryIds, + ...categoriesByCount.map((c) => c.id), + ] + .uniq() + .slice(0, maxCategoriesToDisplay); - assert.equal( - queryAll(".category-link .category-name").text(), - ids.map((i) => categoriesByCount.find((c) => c.id === i).name).join("") - ); - }, -}); - -widgetTest("badges link - disabled", { - template: '{{mount-widget widget="hamburger-menu"}}', - - beforeEach() { - this.siteSettings.enable_badges = false; - }, - - test(assert) { - assert.ok(!queryAll(".badge-link").length); - }, -}); - -widgetTest("badges link", { - template: '{{mount-widget widget="hamburger-menu"}}', - - test(assert) { - assert.ok(queryAll(".badge-link").length); - }, -}); - -widgetTest("user directory link", { - template: '{{mount-widget widget="hamburger-menu"}}', - - test(assert) { - assert.ok(queryAll(".user-directory-link").length); - }, -}); - -widgetTest("user directory link - disabled", { - template: '{{mount-widget widget="hamburger-menu"}}', - - beforeEach() { - this.siteSettings.enable_user_directory = false; - }, - - test(assert) { - assert.ok(!queryAll(".user-directory-link").length); - }, -}); - -widgetTest("general links", { - template: '{{mount-widget widget="hamburger-menu"}}', - - test(assert) { - assert.ok(queryAll(".about-link").length); - assert.ok(queryAll(".keyboard-shortcuts-link").length); - }, + assert.equal( + queryAll(".category-link .category-name").text(), + ids.map((i) => categoriesByCount.find((c) => c.id === i).name).join("") + ); + }, + }); + + componentTest("badges link - disabled", { + template: '{{mount-widget widget="hamburger-menu"}}', + + beforeEach() { + this.siteSettings.enable_badges = false; + }, + + test(assert) { + assert.ok(!queryAll(".badge-link").length); + }, + }); + + componentTest("badges link", { + template: '{{mount-widget widget="hamburger-menu"}}', + + test(assert) { + assert.ok(queryAll(".badge-link").length); + }, + }); + + componentTest("user directory link", { + template: '{{mount-widget widget="hamburger-menu"}}', + + test(assert) { + assert.ok(queryAll(".user-directory-link").length); + }, + }); + + componentTest("user directory link - disabled", { + template: '{{mount-widget widget="hamburger-menu"}}', + + beforeEach() { + this.siteSettings.enable_user_directory = false; + }, + + test(assert) { + assert.ok(!queryAll(".user-directory-link").length); + }, + }); + + componentTest("general links", { + template: '{{mount-widget widget="hamburger-menu"}}', + + test(assert) { + assert.ok(queryAll(".about-link").length); + assert.ok(queryAll(".keyboard-shortcuts-link").length); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/header-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/header-test.js index 65ef33e3b0..b4f74d1746 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/header-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/header-test.js @@ -1,80 +1,84 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { exists } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + exists, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForWidget("header"); +discourseModule("Integration | Component | Widget | header", function (hooks) { + setupRenderingTest(hooks); -widgetTest("rendering basics", { - template: '{{mount-widget widget="header"}}', - test(assert) { - assert.ok(queryAll("header.d-header").length); - assert.ok(queryAll("#site-logo").length); - }, -}); - -widgetTest("sign up / login buttons", { - template: - '{{mount-widget widget="header" showCreateAccount=(action "showCreateAccount") showLogin=(action "showLogin") args=args}}', - anonymous: true, - - beforeEach() { - this.set("args", { canSignUp: true }); - this.on("showCreateAccount", () => (this.signupShown = true)); - this.on("showLogin", () => (this.loginShown = true)); - }, - - async test(assert) { - assert.ok(queryAll("button.sign-up-button").length); - assert.ok(queryAll("button.login-button").length); - - await click("button.sign-up-button"); - assert.ok(this.signupShown); - - await click("button.login-button"); - assert.ok(this.loginShown); - }, -}); - -widgetTest("anon when login required", { - template: - '{{mount-widget widget="header" showCreateAccount=(action "showCreateAccount") showLogin=(action "showLogin") args=args}}', - anonymous: true, - - beforeEach() { - this.set("args", { canSignUp: true }); - this.on("showCreateAccount", () => (this.signupShown = true)); - this.on("showLogin", () => (this.loginShown = true)); - this.siteSettings.login_required = true; - }, - - test(assert) { - assert.ok(exists("button.login-button")); - assert.ok(exists("button.sign-up-button")); - assert.ok(!exists("#search-button")); - assert.ok(!exists("#toggle-hamburger-menu")); - }, -}); - -widgetTest("logged in when login required", { - template: - '{{mount-widget widget="header" showCreateAccount=(action "showCreateAccount") showLogin=(action "showLogin") args=args}}', - - beforeEach() { - this.set("args", { canSignUp: true }); - this.on("showCreateAccount", () => (this.signupShown = true)); - this.on("showLogin", () => (this.loginShown = true)); - this.siteSettings.login_required = true; - }, - - test(assert) { - assert.ok(!exists("button.login-button")); - assert.ok(!exists("button.sign-up-button")); - assert.ok(exists("#search-button")); - assert.ok(exists("#toggle-hamburger-menu")); - assert.ok(exists("#current-user")); - }, + componentTest("rendering basics", { + template: '{{mount-widget widget="header"}}', + test(assert) { + assert.ok(queryAll("header.d-header").length); + assert.ok(queryAll("#site-logo").length); + }, + }); + + componentTest("sign up / login buttons", { + template: + '{{mount-widget widget="header" showCreateAccount=(action "showCreateAccount") showLogin=(action "showLogin") args=args}}', + anonymous: true, + + beforeEach() { + this.set("args", { canSignUp: true }); + this.on("showCreateAccount", () => (this.signupShown = true)); + this.on("showLogin", () => (this.loginShown = true)); + }, + + async test(assert) { + assert.ok(queryAll("button.sign-up-button").length); + assert.ok(queryAll("button.login-button").length); + + await click("button.sign-up-button"); + assert.ok(this.signupShown); + + await click("button.login-button"); + assert.ok(this.loginShown); + }, + }); + + componentTest("anon when login required", { + template: + '{{mount-widget widget="header" showCreateAccount=(action "showCreateAccount") showLogin=(action "showLogin") args=args}}', + anonymous: true, + + beforeEach() { + this.set("args", { canSignUp: true }); + this.on("showCreateAccount", () => (this.signupShown = true)); + this.on("showLogin", () => (this.loginShown = true)); + this.siteSettings.login_required = true; + }, + + test(assert) { + assert.ok(exists("button.login-button")); + assert.ok(exists("button.sign-up-button")); + assert.ok(!exists("#search-button")); + assert.ok(!exists("#toggle-hamburger-menu")); + }, + }); + + componentTest("logged in when login required", { + template: + '{{mount-widget widget="header" showCreateAccount=(action "showCreateAccount") showLogin=(action "showLogin") args=args}}', + + beforeEach() { + this.set("args", { canSignUp: true }); + this.on("showCreateAccount", () => (this.signupShown = true)); + this.on("showLogin", () => (this.loginShown = true)); + this.siteSettings.login_required = true; + }, + + test(assert) { + assert.ok(!exists("button.login-button")); + assert.ok(!exists("button.sign-up-button")); + assert.ok(exists("#search-button")); + assert.ok(exists("#toggle-hamburger-menu")); + assert.ok(exists("#current-user")); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/home-logo-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/home-logo-test.js index 3dd98ffc58..91046ece55 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/home-logo-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/home-logo-test.js @@ -1,10 +1,11 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import Session from "discourse/models/session"; -moduleForWidget("home-logo"); const bigLogo = "/images/d-logo-sketch.png?test"; const smallLogo = "/images/d-logo-sketch-small.png?test"; @@ -13,234 +14,240 @@ const darkLogo = "/images/d-logo-sketch.png?dark"; const title = "Cool Forum"; const prefersDark = "(prefers-color-scheme: dark)"; -widgetTest("basics", { - template: '{{mount-widget widget="home-logo" args=args}}', - skip: true, - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_small_url = smallLogo; - this.siteSettings.title = title; - this.set("args", { minimized: false }); - }, +discourseModule("Integration | Component | Widget | home-logo", function ( + hooks +) { + setupRenderingTest(hooks); - test(assert) { - assert.ok(queryAll(".title").length === 1); + componentTest("basics", { + template: '{{mount-widget widget="home-logo" args=args}}', + skip: true, + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_small_url = smallLogo; + this.siteSettings.title = title; + this.set("args", { minimized: false }); + }, - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal(queryAll("#site-logo").attr("src"), bigLogo); - assert.equal(queryAll("#site-logo").attr("alt"), title); - }, -}); - -widgetTest("basics - minimized", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_small_url = smallLogo; - this.siteSettings.title = title; - this.set("args", { minimized: true }); - }, - - test(assert) { - assert.ok(queryAll("img.logo-small").length === 1); - assert.equal(queryAll("img.logo-small").attr("src"), smallLogo); - assert.equal(queryAll("img.logo-small").attr("alt"), title); - assert.equal(queryAll("img.logo-small").attr("width"), 36); - }, -}); - -widgetTest("no logo", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = ""; - this.siteSettings.site_logo_small_url = ""; - this.siteSettings.title = title; - this.set("args", { minimized: false }); - }, - - test(assert) { - assert.ok(queryAll("h1#site-text-logo.text-logo").length === 1); - assert.equal(queryAll("#site-text-logo").text(), title); - }, -}); - -widgetTest("no logo - minimized", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = ""; - this.siteSettings.site_logo_small_url = ""; - this.siteSettings.title = title; - this.set("args", { minimized: true }); - }, - - test(assert) { - assert.ok(queryAll(".d-icon-home").length === 1); - }, -}); - -widgetTest("mobile logo", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_mobile_logo_url = mobileLogo; - this.siteSettings.site_logo_small_url = smallLogo; - this.site.mobileView = true; - }, - - test(assert) { - assert.ok(queryAll("img#site-logo.logo-mobile").length === 1); - assert.equal(queryAll("#site-logo").attr("src"), mobileLogo); - }, -}); - -widgetTest("mobile without logo", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.site.mobileView = true; - }, - - test(assert) { - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal(queryAll("#site-logo").attr("src"), bigLogo); - }, -}); - -widgetTest("logo with dark mode alternative", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_dark_url = darkLogo; - Session.currentProp("darkModeAvailable", true); - }, - afterEach() { - Session.currentProp("darkModeAvailable", null); - }, - - test(assert) { - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal(queryAll("#site-logo").attr("src"), bigLogo); - - assert.equal( - queryAll("picture source").attr("media"), - prefersDark, - "includes dark mode media attribute" - ); - assert.equal( - queryAll("picture source").attr("srcset"), - darkLogo, - "includes dark mode alternative logo source" - ); - }, -}); - -widgetTest("mobile logo with dark mode alternative", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_mobile_logo_url = mobileLogo; - this.siteSettings.site_mobile_logo_dark_url = darkLogo; - Session.currentProp("darkModeAvailable", true); - - this.site.mobileView = true; - }, - afterEach() { - Session.currentProp("darkModeAvailable", null); - }, - - test(assert) { - assert.equal(queryAll("#site-logo").attr("src"), mobileLogo); - - assert.equal( - queryAll("picture source").attr("media"), - prefersDark, - "includes dark mode media attribute" - ); - assert.equal( - queryAll("picture source").attr("srcset"), - darkLogo, - "includes dark mode alternative logo source" - ); - }, -}); - -widgetTest("dark mode enabled but no dark logo set", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_dark_url = ""; - Session.currentProp("darkModeAvailable", true); - }, - afterEach() { - Session.currentProp("darkModeAvailable", null); - }, - - test(assert) { - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal(queryAll("#site-logo").attr("src"), bigLogo); - assert.ok( - queryAll("picture").length === 0, - "does not include alternative logo" - ); - }, -}); - -widgetTest("dark logo set but no dark mode", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_dark_url = darkLogo; - }, - - test(assert) { - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal(queryAll("#site-logo").attr("src"), bigLogo); - assert.ok( - queryAll("picture").length === 0, - "does not include alternative logo" - ); - }, -}); - -widgetTest("dark color scheme and dark logo set", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_dark_url = darkLogo; - Session.currentProp("defaultColorSchemeIsDark", true); - }, - afterEach() { - Session.currentProp("defaultColorSchemeIsDark", null); - }, - test(assert) { - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal( - queryAll("#site-logo").attr("src"), - darkLogo, - "uses dark logo" - ); - assert.ok( - queryAll("picture").length === 0, - "does not add dark mode alternative" - ); - }, -}); - -widgetTest("dark color scheme and dark logo not set", { - template: '{{mount-widget widget="home-logo" args=args}}', - beforeEach() { - this.siteSettings.site_logo_url = bigLogo; - this.siteSettings.site_logo_dark_url = ""; - Session.currentProp("defaultColorSchemeIsDark", true); - }, - afterEach() { - Session.currentProp("defaultColorSchemeIsDark", null); - }, - test(assert) { - assert.ok(queryAll("img#site-logo.logo-big").length === 1); - assert.equal( - queryAll("#site-logo").attr("src"), - bigLogo, - "uses regular logo on dark scheme if no dark logo" - ); - }, + test(assert) { + assert.ok(queryAll(".title").length === 1); + + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal(queryAll("#site-logo").attr("src"), bigLogo); + assert.equal(queryAll("#site-logo").attr("alt"), title); + }, + }); + + componentTest("basics - minimized", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_small_url = smallLogo; + this.siteSettings.title = title; + this.set("args", { minimized: true }); + }, + + test(assert) { + assert.ok(queryAll("img.logo-small").length === 1); + assert.equal(queryAll("img.logo-small").attr("src"), smallLogo); + assert.equal(queryAll("img.logo-small").attr("alt"), title); + assert.equal(queryAll("img.logo-small").attr("width"), 36); + }, + }); + + componentTest("no logo", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = ""; + this.siteSettings.site_logo_small_url = ""; + this.siteSettings.title = title; + this.set("args", { minimized: false }); + }, + + test(assert) { + assert.ok(queryAll("h1#site-text-logo.text-logo").length === 1); + assert.equal(queryAll("#site-text-logo").text(), title); + }, + }); + + componentTest("no logo - minimized", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = ""; + this.siteSettings.site_logo_small_url = ""; + this.siteSettings.title = title; + this.set("args", { minimized: true }); + }, + + test(assert) { + assert.ok(queryAll(".d-icon-home").length === 1); + }, + }); + + componentTest("mobile logo", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_mobile_logo_url = mobileLogo; + this.siteSettings.site_logo_small_url = smallLogo; + this.site.mobileView = true; + }, + + test(assert) { + assert.ok(queryAll("img#site-logo.logo-mobile").length === 1); + assert.equal(queryAll("#site-logo").attr("src"), mobileLogo); + }, + }); + + componentTest("mobile without logo", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.site.mobileView = true; + }, + + test(assert) { + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal(queryAll("#site-logo").attr("src"), bigLogo); + }, + }); + + componentTest("logo with dark mode alternative", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_dark_url = darkLogo; + Session.currentProp("darkModeAvailable", true); + }, + afterEach() { + Session.currentProp("darkModeAvailable", null); + }, + + test(assert) { + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal(queryAll("#site-logo").attr("src"), bigLogo); + + assert.equal( + queryAll("picture source").attr("media"), + prefersDark, + "includes dark mode media attribute" + ); + assert.equal( + queryAll("picture source").attr("srcset"), + darkLogo, + "includes dark mode alternative logo source" + ); + }, + }); + + componentTest("mobile logo with dark mode alternative", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_mobile_logo_url = mobileLogo; + this.siteSettings.site_mobile_logo_dark_url = darkLogo; + Session.currentProp("darkModeAvailable", true); + + this.site.mobileView = true; + }, + afterEach() { + Session.currentProp("darkModeAvailable", null); + }, + + test(assert) { + assert.equal(queryAll("#site-logo").attr("src"), mobileLogo); + + assert.equal( + queryAll("picture source").attr("media"), + prefersDark, + "includes dark mode media attribute" + ); + assert.equal( + queryAll("picture source").attr("srcset"), + darkLogo, + "includes dark mode alternative logo source" + ); + }, + }); + + componentTest("dark mode enabled but no dark logo set", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_dark_url = ""; + Session.currentProp("darkModeAvailable", true); + }, + afterEach() { + Session.currentProp("darkModeAvailable", null); + }, + + test(assert) { + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal(queryAll("#site-logo").attr("src"), bigLogo); + assert.ok( + queryAll("picture").length === 0, + "does not include alternative logo" + ); + }, + }); + + componentTest("dark logo set but no dark mode", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_dark_url = darkLogo; + }, + + test(assert) { + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal(queryAll("#site-logo").attr("src"), bigLogo); + assert.ok( + queryAll("picture").length === 0, + "does not include alternative logo" + ); + }, + }); + + componentTest("dark color scheme and dark logo set", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_dark_url = darkLogo; + Session.currentProp("defaultColorSchemeIsDark", true); + }, + afterEach() { + Session.currentProp("defaultColorSchemeIsDark", null); + }, + test(assert) { + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal( + queryAll("#site-logo").attr("src"), + darkLogo, + "uses dark logo" + ); + assert.ok( + queryAll("picture").length === 0, + "does not add dark mode alternative" + ); + }, + }); + + componentTest("dark color scheme and dark logo not set", { + template: '{{mount-widget widget="home-logo" args=args}}', + beforeEach() { + this.siteSettings.site_logo_url = bigLogo; + this.siteSettings.site_logo_dark_url = ""; + Session.currentProp("defaultColorSchemeIsDark", true); + }, + afterEach() { + Session.currentProp("defaultColorSchemeIsDark", null); + }, + test(assert) { + assert.ok(queryAll("img#site-logo.logo-big").length === 1); + assert.equal( + queryAll("#site-logo").attr("src"), + bigLogo, + "uses regular logo on dark scheme if no dark logo" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/post-links-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/post-links-test.js index 8925ea0b29..43510cf083 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/post-links-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/post-links-test.js @@ -1,59 +1,65 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { click } from "@ember/test-helpers"; -moduleForWidget("post-links"); +discourseModule("Integration | Component | Widget | post-links", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("duplicate links", { - template: '{{mount-widget widget="post-links" args=args}}', - beforeEach() { - this.set("args", { - id: 2, - links: [ - { - title: "Evil Trout Link", - url: "http://eviltrout.com", - reflection: true, - }, - { - title: "Evil Trout Link", - url: "http://dupe.eviltrout.com", - reflection: true, - }, - ], - }); - }, - test(assert) { - assert.equal( - queryAll(".post-links a.track-link").length, - 1, - "it hides the dupe link" - ); - }, -}); + componentTest("duplicate links", { + template: '{{mount-widget widget="post-links" args=args}}', + beforeEach() { + this.set("args", { + id: 2, + links: [ + { + title: "Evil Trout Link", + url: "http://eviltrout.com", + reflection: true, + }, + { + title: "Evil Trout Link", + url: "http://dupe.eviltrout.com", + reflection: true, + }, + ], + }); + }, + test(assert) { + assert.equal( + queryAll(".post-links a.track-link").length, + 1, + "it hides the dupe link" + ); + }, + }); -widgetTest("collapsed links", { - template: '{{mount-widget widget="post-links" args=args}}', - beforeEach() { - this.set("args", { - id: 1, - links: [ - { title: "Link 1", url: "http://eviltrout.com?1", reflection: true }, - { title: "Link 2", url: "http://eviltrout.com?2", reflection: true }, - { title: "Link 3", url: "http://eviltrout.com?3", reflection: true }, - { title: "Link 4", url: "http://eviltrout.com?4", reflection: true }, - { title: "Link 5", url: "http://eviltrout.com?5", reflection: true }, - { title: "Link 6", url: "http://eviltrout.com?6", reflection: true }, - { title: "Link 7", url: "http://eviltrout.com?7", reflection: true }, - ], - }); - }, - async test(assert) { - assert.ok(queryAll(".expand-links").length === 1, "collapsed by default"); - await click("a.expand-links"); - assert.equal(queryAll(".post-links a.track-link").length, 7); - }, + componentTest("collapsed links", { + template: '{{mount-widget widget="post-links" args=args}}', + beforeEach() { + this.set("args", { + id: 1, + links: [ + { title: "Link 1", url: "http://eviltrout.com?1", reflection: true }, + { title: "Link 2", url: "http://eviltrout.com?2", reflection: true }, + { title: "Link 3", url: "http://eviltrout.com?3", reflection: true }, + { title: "Link 4", url: "http://eviltrout.com?4", reflection: true }, + { title: "Link 5", url: "http://eviltrout.com?5", reflection: true }, + { title: "Link 6", url: "http://eviltrout.com?6", reflection: true }, + { title: "Link 7", url: "http://eviltrout.com?7", reflection: true }, + ], + }); + }, + async test(assert) { + assert.ok(queryAll(".expand-links").length === 1, "collapsed by default"); + await click("a.expand-links"); + assert.equal(queryAll(".post-links a.track-link").length, 7); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/post-menu-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/post-menu-test.js index ced502f879..2bf649baeb 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/post-menu-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/post-menu-test.js @@ -1,48 +1,54 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import { withPluginApi } from "discourse/lib/plugin-api"; -moduleForWidget("post-menu"); +discourseModule("Integration | Component | Widget | post-menu", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("add extra button", { - template: '{{mount-widget widget="post-menu" args=args}}', - beforeEach() { - this.set("args", {}); - withPluginApi("0.8", (api) => { - api.addPostMenuButton("coffee", () => { - return { - action: "drinkCoffee", - icon: "coffee", - className: "hot-coffee", - title: "coffee.title", - position: "first", - }; + componentTest("add extra button", { + template: '{{mount-widget widget="post-menu" args=args}}', + beforeEach() { + this.set("args", {}); + withPluginApi("0.8", (api) => { + api.addPostMenuButton("coffee", () => { + return { + action: "drinkCoffee", + icon: "coffee", + className: "hot-coffee", + title: "coffee.title", + position: "first", + }; + }); }); - }); - }, - async test(assert) { - assert.ok( - queryAll(".actions .extra-buttons .hot-coffee").length === 1, - "It renders extra button" - ); - }, -}); + }, + async test(assert) { + assert.ok( + queryAll(".actions .extra-buttons .hot-coffee").length === 1, + "It renders extra button" + ); + }, + }); -widgetTest("remove extra button", { - template: '{{mount-widget widget="post-menu" args=args}}', - beforeEach() { - this.set("args", {}); - withPluginApi("0.8", (api) => { - api.removePostMenuButton("coffee"); - }); - }, - async test(assert) { - assert.ok( - queryAll(".actions .extra-buttons .hot-coffee").length === 0, - "It doesn't removes coffee button" - ); - }, + componentTest("remove extra button", { + template: '{{mount-widget widget="post-menu" args=args}}', + beforeEach() { + this.set("args", {}); + withPluginApi("0.8", (api) => { + api.removePostMenuButton("coffee"); + }); + }, + async test(assert) { + assert.ok( + queryAll(".actions .extra-buttons .hot-coffee").length === 0, + "It doesn't removes coffee button" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/post-stream-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/post-stream-test.js index b1baeaa1c6..1fb14aa1da 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/post-stream-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/post-stream-test.js @@ -1,15 +1,15 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import Topic from "discourse/models/topic"; import Post from "discourse/models/post"; -moduleForWidget("post-stream"); - function postStreamTest(name, attrs) { - widgetTest(name, { + componentTest(name, { template: `{{mount-widget widget="post-stream" args=(hash posts=posts)}}`, beforeEach() { const site = this.container.lookup("site:main"); @@ -21,130 +21,136 @@ function postStreamTest(name, attrs) { }); } -postStreamTest("basics", { - posts() { - const site = this.container.lookup("site:main"); - const topic = Topic.create(); - topic.set("details.created_by", { id: 123 }); - return [ - Post.create({ - topic, - id: 1, - post_number: 1, - user_id: 123, - primary_group_name: "trout", - avatar_template: "/images/avatar.png", - }), - Post.create({ - topic, - id: 2, - post_number: 2, - post_type: site.get("post_types.moderator_action"), - }), - Post.create({ topic, id: 3, post_number: 3, hidden: true }), - Post.create({ - topic, - id: 4, - post_number: 4, - post_type: site.get("post_types.whisper"), - }), - Post.create({ - topic, - id: 5, - post_number: 5, - wiki: true, - via_email: true, - }), - Post.create({ - topic, - id: 6, - post_number: 6, - via_email: true, - is_auto_generated: true, - }), - ]; - }, +discourseModule("Integration | Component | Widget | post-stream", function ( + hooks +) { + setupRenderingTest(hooks); - test(assert) { - assert.equal(queryAll(".post-stream").length, 1); - assert.equal(queryAll(".topic-post").length, 6, "renders all posts"); + postStreamTest("basics", { + posts() { + const site = this.container.lookup("site:main"); + const topic = Topic.create(); + topic.set("details.created_by", { id: 123 }); + return [ + Post.create({ + topic, + id: 1, + post_number: 1, + user_id: 123, + primary_group_name: "trout", + avatar_template: "/images/avatar.png", + }), + Post.create({ + topic, + id: 2, + post_number: 2, + post_type: site.get("post_types.moderator_action"), + }), + Post.create({ topic, id: 3, post_number: 3, hidden: true }), + Post.create({ + topic, + id: 4, + post_number: 4, + post_type: site.get("post_types.whisper"), + }), + Post.create({ + topic, + id: 5, + post_number: 5, + wiki: true, + via_email: true, + }), + Post.create({ + topic, + id: 6, + post_number: 6, + via_email: true, + is_auto_generated: true, + }), + ]; + }, - // look for special class bindings - assert.equal( - queryAll(".topic-post:eq(0).topic-owner").length, - 1, - "it applies the topic owner class" - ); - assert.equal( - queryAll(".topic-post:eq(0).group-trout").length, - 1, - "it applies the primary group class" - ); - assert.equal( - queryAll(".topic-post:eq(0).regular").length, - 1, - "it applies the regular class" - ); - assert.equal( - queryAll(".topic-post:eq(1).moderator").length, - 1, - "it applies the moderator class" - ); - assert.equal( - queryAll(".topic-post:eq(2).post-hidden").length, - 1, - "it applies the hidden class" - ); - assert.equal( - queryAll(".topic-post:eq(3).whisper").length, - 1, - "it applies the whisper class" - ); - assert.equal( - queryAll(".topic-post:eq(4).wiki").length, - 1, - "it applies the wiki class" - ); + test(assert) { + assert.equal(queryAll(".post-stream").length, 1); + assert.equal(queryAll(".topic-post").length, 6, "renders all posts"); - // it renders an article for the body with appropriate attributes - assert.equal(queryAll("article#post_2").length, 1); - assert.equal(queryAll("article[data-user-id=123]").length, 1); - assert.equal(queryAll("article[data-post-id=3]").length, 1); - assert.equal(queryAll("article#post_5.via-email").length, 1); - assert.equal(queryAll("article#post_6.is-auto-generated").length, 1); + // look for special class bindings + assert.equal( + queryAll(".topic-post:eq(0).topic-owner").length, + 1, + "it applies the topic owner class" + ); + assert.equal( + queryAll(".topic-post:eq(0).group-trout").length, + 1, + "it applies the primary group class" + ); + assert.equal( + queryAll(".topic-post:eq(0).regular").length, + 1, + "it applies the regular class" + ); + assert.equal( + queryAll(".topic-post:eq(1).moderator").length, + 1, + "it applies the moderator class" + ); + assert.equal( + queryAll(".topic-post:eq(2).post-hidden").length, + 1, + "it applies the hidden class" + ); + assert.equal( + queryAll(".topic-post:eq(3).whisper").length, + 1, + "it applies the whisper class" + ); + assert.equal( + queryAll(".topic-post:eq(4).wiki").length, + 1, + "it applies the wiki class" + ); - assert.equal( - queryAll("article:eq(0) .main-avatar").length, - 1, - "renders the main avatar" - ); - }, -}); - -postStreamTest("deleted posts", { - posts() { - const topic = Topic.create(); - topic.set("details.created_by", { id: 123 }); - return [ - Post.create({ - topic, - id: 1, - post_number: 1, - deleted_at: new Date().toString(), - }), - ]; - }, - - test(assert) { - assert.equal( - queryAll(".topic-post.deleted").length, - 1, - "it applies the deleted class" - ); - assert.equal( - queryAll(".deleted-user-avatar").length, - 1, - "it has the trash avatar" - ); - }, + // it renders an article for the body with appropriate attributes + assert.equal(queryAll("article#post_2").length, 1); + assert.equal(queryAll("article[data-user-id=123]").length, 1); + assert.equal(queryAll("article[data-post-id=3]").length, 1); + assert.equal(queryAll("article#post_5.via-email").length, 1); + assert.equal(queryAll("article#post_6.is-auto-generated").length, 1); + + assert.equal( + queryAll("article:eq(0) .main-avatar").length, + 1, + "renders the main avatar" + ); + }, + }); + + postStreamTest("deleted posts", { + posts() { + const topic = Topic.create(); + topic.set("details.created_by", { id: 123 }); + return [ + Post.create({ + topic, + id: 1, + post_number: 1, + deleted_at: new Date().toString(), + }), + ]; + }, + + test(assert) { + assert.equal( + queryAll(".topic-post.deleted").length, + 1, + "it applies the deleted class" + ); + assert.equal( + queryAll(".deleted-user-avatar").length, + 1, + "it has the trash avatar" + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/post-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/post-test.js index b85f37e1d1..4bd114a1c4 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/post-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/post-test.js @@ -1,991 +1,1033 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import I18n from "I18n"; import EmberObject from "@ember/object"; -import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; import { click } from "@ember/test-helpers"; -moduleForWidget("post"); +discourseModule("Integration | Component | Widget | post", function (hooks) { + setupRenderingTest(hooks); -widgetTest("basic elements", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { shareUrl: "/example", post_number: 1 }); - }, - test(assert) { - assert.ok(queryAll(".names").length, "includes poster name"); + componentTest("basic elements", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { shareUrl: "/example", post_number: 1 }); + }, + test(assert) { + assert.ok(queryAll(".names").length, "includes poster name"); - assert.ok(queryAll("a.post-date").length, "includes post date"); - assert.ok(queryAll("a.post-date[data-share-url]").length); - assert.ok(queryAll("a.post-date[data-post-number]").length); - }, -}); + assert.ok(queryAll("a.post-date").length, "includes post date"); + assert.ok(queryAll("a.post-date[data-share-url]").length); + assert.ok(queryAll("a.post-date[data-post-number]").length); + }, + }); -widgetTest("post - links", { - template: '{{mount-widget widget="post-contents" args=args}}', - beforeEach() { - this.set("args", { - cooked: - "first link and second link", - linkCounts: [ - { url: "http://link1.example.com/", clicks: 1, internal: true }, - { url: "http://link2.example.com/", clicks: 2, internal: true }, - ], - }); - }, - async test(assert) { - assert.equal(queryAll(".badge.clicks:nth(0)").text(), "1"); - assert.equal(queryAll(".badge.clicks:nth(1)").text(), "2"); - }, -}); + componentTest("post - links", { + template: '{{mount-widget widget="post-contents" args=args}}', + beforeEach() { + this.set("args", { + cooked: + "first link and second link", + linkCounts: [ + { url: "http://link1.example.com/", clicks: 1, internal: true }, + { url: "http://link2.example.com/", clicks: 2, internal: true }, + ], + }); + }, + async test(assert) { + assert.equal(queryAll(".badge.clicks:nth(0)").text(), "1"); + assert.equal(queryAll(".badge.clicks:nth(1)").text(), "2"); + }, + }); -widgetTest("wiki", { - template: - '{{mount-widget widget="post" args=args showHistory=(action "showHistory")}}', - beforeEach() { - this.set("args", { wiki: true, version: 2, canViewEditHistory: true }); - this.on("showHistory", () => (this.historyShown = true)); - }, - async test(assert) { - await click(".post-info .wiki"); - assert.ok( - this.historyShown, - "clicking the wiki icon displays the post history" - ); - }, -}); + componentTest("wiki", { + template: + '{{mount-widget widget="post" args=args showHistory=(action "showHistory")}}', + beforeEach() { + this.set("args", { wiki: true, version: 2, canViewEditHistory: true }); + this.on("showHistory", () => (this.historyShown = true)); + }, + async test(assert) { + await click(".post-info .wiki"); + assert.ok( + this.historyShown, + "clicking the wiki icon displays the post history" + ); + }, + }); -widgetTest("wiki without revision", { - template: - '{{mount-widget widget="post" args=args editPost=(action "editPost")}}', - beforeEach() { - this.set("args", { wiki: true, version: 1, canViewEditHistory: true }); - this.on("editPost", () => (this.editPostCalled = true)); - }, - async test(assert) { - await click(".post-info .wiki"); - assert.ok(this.editPostCalled, "clicking wiki icon edits the post"); - }, -}); + componentTest("wiki without revision", { + template: + '{{mount-widget widget="post" args=args editPost=(action "editPost")}}', + beforeEach() { + this.set("args", { wiki: true, version: 1, canViewEditHistory: true }); + this.on("editPost", () => (this.editPostCalled = true)); + }, + async test(assert) { + await click(".post-info .wiki"); + assert.ok(this.editPostCalled, "clicking wiki icon edits the post"); + }, + }); -widgetTest("via-email", { - template: - '{{mount-widget widget="post" args=args showRawEmail=(action "showRawEmail")}}', - beforeEach() { - this.set("args", { via_email: true, canViewRawEmail: true }); - this.on("showRawEmail", () => (this.rawEmailShown = true)); - }, - async test(assert) { - await click(".post-info.via-email"); - assert.ok(this.rawEmailShown, "clicking the envelope shows the raw email"); - }, -}); + componentTest("via-email", { + template: + '{{mount-widget widget="post" args=args showRawEmail=(action "showRawEmail")}}', + beforeEach() { + this.set("args", { via_email: true, canViewRawEmail: true }); + this.on("showRawEmail", () => (this.rawEmailShown = true)); + }, + async test(assert) { + await click(".post-info.via-email"); + assert.ok( + this.rawEmailShown, + "clicking the envelope shows the raw email" + ); + }, + }); -widgetTest("via-email without permission", { - template: - '{{mount-widget widget="post" args=args showRawEmail=(action "showRawEmail")}}', - beforeEach() { - this.set("args", { via_email: true, canViewRawEmail: false }); - this.on("showRawEmail", () => (this.rawEmailShown = true)); - }, - async test(assert) { - await click(".post-info.via-email"); - assert.ok( - !this.rawEmailShown, - "clicking the envelope doesn't show the raw email" - ); - }, -}); + componentTest("via-email without permission", { + template: + '{{mount-widget widget="post" args=args showRawEmail=(action "showRawEmail")}}', + beforeEach() { + this.set("args", { via_email: true, canViewRawEmail: false }); + this.on("showRawEmail", () => (this.rawEmailShown = true)); + }, + async test(assert) { + await click(".post-info.via-email"); + assert.ok( + !this.rawEmailShown, + "clicking the envelope doesn't show the raw email" + ); + }, + }); -widgetTest("history", { - template: - '{{mount-widget widget="post" args=args showHistory=(action "showHistory")}}', - beforeEach() { - this.set("args", { version: 3, canViewEditHistory: true }); - this.on("showHistory", () => (this.historyShown = true)); - }, - async test(assert) { - await click(".post-info.edits"); - assert.ok(this.historyShown, "clicking the pencil shows the history"); - }, -}); + componentTest("history", { + template: + '{{mount-widget widget="post" args=args showHistory=(action "showHistory")}}', + beforeEach() { + this.set("args", { version: 3, canViewEditHistory: true }); + this.on("showHistory", () => (this.historyShown = true)); + }, + async test(assert) { + await click(".post-info.edits"); + assert.ok(this.historyShown, "clicking the pencil shows the history"); + }, + }); -widgetTest("history without view permission", { - template: - '{{mount-widget widget="post" args=args showHistory=(action "showHistory")}}', - beforeEach() { - this.set("args", { version: 3, canViewEditHistory: false }); - this.on("showHistory", () => (this.historyShown = true)); - }, - async test(assert) { - await click(".post-info.edits"); - assert.ok( - !this.historyShown, - `clicking the pencil doesn't show the history` - ); - }, -}); + componentTest("history without view permission", { + template: + '{{mount-widget widget="post" args=args showHistory=(action "showHistory")}}', + beforeEach() { + this.set("args", { version: 3, canViewEditHistory: false }); + this.on("showHistory", () => (this.historyShown = true)); + }, + async test(assert) { + await click(".post-info.edits"); + assert.ok( + !this.historyShown, + `clicking the pencil doesn't show the history` + ); + }, + }); -widgetTest("whisper", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { isWhisper: true }); - }, - test(assert) { - assert.ok(queryAll(".topic-post.whisper").length === 1); - assert.ok(queryAll(".post-info.whisper").length === 1); - }, -}); + componentTest("whisper", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { isWhisper: true }); + }, + test(assert) { + assert.ok(queryAll(".topic-post.whisper").length === 1); + assert.ok(queryAll(".post-info.whisper").length === 1); + }, + }); -widgetTest("like count button", { - template: '{{mount-widget widget="post" model=post args=args}}', - beforeEach(store) { - const topic = store.createRecord("topic", { id: 123 }); - const post = store.createRecord("post", { - id: 1, - post_number: 1, - topic, - like_count: 3, - actions_summary: [{ id: 2, count: 1, hidden: false, can_act: true }], - }); - this.set("post", post); - this.set("args", { likeCount: 1 }); - }, - async test(assert) { - assert.ok(queryAll("button.like-count").length === 1); - assert.ok(queryAll(".who-liked").length === 0); + componentTest("like count button", { + template: '{{mount-widget widget="post" model=post args=args}}', + beforeEach(store) { + const topic = store.createRecord("topic", { id: 123 }); + const post = store.createRecord("post", { + id: 1, + post_number: 1, + topic, + like_count: 3, + actions_summary: [{ id: 2, count: 1, hidden: false, can_act: true }], + }); + this.set("post", post); + this.set("args", { likeCount: 1 }); + }, + async test(assert) { + assert.ok(queryAll("button.like-count").length === 1); + assert.ok(queryAll(".who-liked").length === 0); - // toggle it on - await click("button.like-count"); - assert.ok(queryAll(".who-liked").length === 1); - assert.ok(queryAll(".who-liked a.trigger-user-card").length === 1); + // toggle it on + await click("button.like-count"); + assert.ok(queryAll(".who-liked").length === 1); + assert.ok(queryAll(".who-liked a.trigger-user-card").length === 1); - // toggle it off - await click("button.like-count"); - assert.ok(queryAll(".who-liked").length === 0); - assert.ok(queryAll(".who-liked a.trigger-user-card").length === 0); - }, -}); + // toggle it off + await click("button.like-count"); + assert.ok(queryAll(".who-liked").length === 0); + assert.ok(queryAll(".who-liked a.trigger-user-card").length === 0); + }, + }); -widgetTest(`like count with no likes`, { - template: '{{mount-widget widget="post" model=post args=args}}', - beforeEach() { - this.set("args", { likeCount: 0 }); - }, - test(assert) { - assert.ok(queryAll("button.like-count").length === 0); - }, -}); + componentTest(`like count with no likes`, { + template: '{{mount-widget widget="post" model=post args=args}}', + beforeEach() { + this.set("args", { likeCount: 0 }); + }, + test(assert) { + assert.ok(queryAll("button.like-count").length === 0); + }, + }); -widgetTest("share button", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { shareUrl: "http://share-me.example.com" }); - }, - test(assert) { - assert.ok( - !!queryAll(".actions button[data-share-url]").length, - "it renders a share button" - ); - }, -}); + componentTest("share button", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { shareUrl: "http://share-me.example.com" }); + }, + test(assert) { + assert.ok( + !!queryAll(".actions button[data-share-url]").length, + "it renders a share button" + ); + }, + }); -widgetTest("liking", { - template: - '{{mount-widget widget="post-menu" args=args toggleLike=(action "toggleLike")}}', - beforeEach() { - const args = { showLike: true, canToggleLike: true }; - this.set("args", args); - this.on("toggleLike", () => { - args.liked = !args.liked; - args.likeCount = args.liked ? 1 : 0; - }); - }, - async test(assert) { - assert.ok(!!queryAll(".actions button.like").length); - assert.ok(queryAll(".actions button.like-count").length === 0); + componentTest("liking", { + template: + '{{mount-widget widget="post-menu" args=args toggleLike=(action "toggleLike")}}', + beforeEach() { + const args = { showLike: true, canToggleLike: true }; + this.set("args", args); + this.on("toggleLike", () => { + args.liked = !args.liked; + args.likeCount = args.liked ? 1 : 0; + }); + }, + async test(assert) { + assert.ok(!!queryAll(".actions button.like").length); + assert.ok(queryAll(".actions button.like-count").length === 0); - await click(".actions button.like"); - assert.ok(!queryAll(".actions button.like").length); - assert.ok(!!queryAll(".actions button.has-like").length); - assert.ok(queryAll(".actions button.like-count").length === 1); + await click(".actions button.like"); + assert.ok(!queryAll(".actions button.like").length); + assert.ok(!!queryAll(".actions button.has-like").length); + assert.ok(queryAll(".actions button.like-count").length === 1); - await click(".actions button.has-like"); - assert.ok(!!queryAll(".actions button.like").length); - assert.ok(!queryAll(".actions button.has-like").length); - assert.ok(queryAll(".actions button.like-count").length === 0); - }, -}); + await click(".actions button.has-like"); + assert.ok(!!queryAll(".actions button.like").length); + assert.ok(!queryAll(".actions button.has-like").length); + assert.ok(queryAll(".actions button.like-count").length === 0); + }, + }); -widgetTest("anon liking", { - template: - '{{mount-widget widget="post-menu" args=args showLogin=(action "showLogin")}}', - anonymous: true, - beforeEach() { - const args = { showLike: true }; - this.set("args", args); - this.on("showLogin", () => (this.loginShown = true)); - }, - async test(assert) { - assert.ok(!!queryAll(".actions button.like").length); - assert.ok(queryAll(".actions button.like-count").length === 0); + componentTest("anon liking", { + template: + '{{mount-widget widget="post-menu" args=args showLogin=(action "showLogin")}}', + anonymous: true, + beforeEach() { + const args = { showLike: true }; + this.set("args", args); + this.on("showLogin", () => (this.loginShown = true)); + }, + async test(assert) { + assert.ok(!!queryAll(".actions button.like").length); + assert.ok(queryAll(".actions button.like-count").length === 0); - assert.equal( - queryAll("button.like").attr("title"), - I18n.t("post.controls.like"), - `shows the right button title for anonymous users` - ); + assert.equal( + queryAll("button.like").attr("title"), + I18n.t("post.controls.like"), + `shows the right button title for anonymous users` + ); - await click(".actions button.like"); - assert.ok(this.loginShown); - }, -}); + await click(".actions button.like"); + assert.ok(this.loginShown); + }, + }); -widgetTest("edit button", { - template: - '{{mount-widget widget="post" args=args editPost=(action "editPost")}}', - beforeEach() { - this.set("args", { canEdit: true }); - this.on("editPost", () => (this.editPostCalled = true)); - }, - async test(assert) { - await click("button.edit"); - assert.ok(this.editPostCalled, "it triggered the edit action"); - }, -}); + componentTest("edit button", { + template: + '{{mount-widget widget="post" args=args editPost=(action "editPost")}}', + beforeEach() { + this.set("args", { canEdit: true }); + this.on("editPost", () => (this.editPostCalled = true)); + }, + async test(assert) { + await click("button.edit"); + assert.ok(this.editPostCalled, "it triggered the edit action"); + }, + }); -widgetTest(`edit button - can't edit`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canEdit: false }); - }, - test(assert) { - assert.equal(queryAll("button.edit").length, 0, `button is not displayed`); - }, -}); + componentTest(`edit button - can't edit`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canEdit: false }); + }, + test(assert) { + assert.equal( + queryAll("button.edit").length, + 0, + `button is not displayed` + ); + }, + }); -widgetTest("recover button", { - template: - '{{mount-widget widget="post" args=args deletePost=(action "deletePost")}}', - beforeEach() { - this.set("args", { canDelete: true }); - this.on("deletePost", () => (this.deletePostCalled = true)); - }, - async test(assert) { - await click("button.delete"); - assert.ok(this.deletePostCalled, "it triggered the delete action"); - }, -}); + componentTest("recover button", { + template: + '{{mount-widget widget="post" args=args deletePost=(action "deletePost")}}', + beforeEach() { + this.set("args", { canDelete: true }); + this.on("deletePost", () => (this.deletePostCalled = true)); + }, + async test(assert) { + await click("button.delete"); + assert.ok(this.deletePostCalled, "it triggered the delete action"); + }, + }); -widgetTest("delete topic button", { - template: - '{{mount-widget widget="post" args=args deletePost=(action "deletePost")}}', - beforeEach() { - this.set("args", { canDeleteTopic: true }); - this.on("deletePost", () => (this.deletePostCalled = true)); - }, - async test(assert) { - await click("button.delete"); - assert.ok(this.deletePostCalled, "it triggered the delete action"); - }, -}); + componentTest("delete topic button", { + template: + '{{mount-widget widget="post" args=args deletePost=(action "deletePost")}}', + beforeEach() { + this.set("args", { canDeleteTopic: true }); + this.on("deletePost", () => (this.deletePostCalled = true)); + }, + async test(assert) { + await click("button.delete"); + assert.ok(this.deletePostCalled, "it triggered the delete action"); + }, + }); -widgetTest(`delete topic button - can't delete`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canDeleteTopic: false }); - }, - test(assert) { - assert.equal( - queryAll("button.delete").length, - 0, - `button is not displayed` - ); - }, -}); + componentTest(`delete topic button - can't delete`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canDeleteTopic: false }); + }, + test(assert) { + assert.equal( + queryAll("button.delete").length, + 0, + `button is not displayed` + ); + }, + }); -widgetTest( - `delete topic button - can't delete when topic author without permission`, - { + componentTest( + `delete topic button - can't delete when topic author without permission`, + { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + canDeleteTopic: false, + showFlagDelete: true, + canFlag: true, + }); + }, + + async test(assert) { + await click(".show-more-actions"); + + assert.equal( + queryAll("button.create-flag").length, + 1, + `button is displayed` + ); + assert.equal( + queryAll("button.delete").length, + 1, + `button is displayed` + ); + assert.equal( + queryAll("button.delete").attr("title"), + I18n.t("post.controls.delete_topic_disallowed"), + `shows the right button title for users without permissions` + ); + }, + } + ); + + componentTest("recover topic button", { + template: + '{{mount-widget widget="post" args=args recoverPost=(action "recoverPost")}}', + beforeEach() { + this.set("args", { canRecoverTopic: true }); + this.on("recoverPost", () => (this.recovered = true)); + }, + async test(assert) { + await click("button.recover"); + assert.ok(this.recovered); + }, + }); + + componentTest(`recover topic button - can't recover`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canRecoverTopic: false }); + }, + test(assert) { + assert.equal( + queryAll("button.recover").length, + 0, + `button is not displayed` + ); + }, + }); + + componentTest("delete post button", { + template: + '{{mount-widget widget="post" args=args deletePost=(action "deletePost")}}', + beforeEach() { + this.set("args", { canDelete: true, canFlag: true }); + this.on("deletePost", () => (this.deletePostCalled = true)); + }, + async test(assert) { + await click(".show-more-actions"); + await click("button.delete"); + assert.ok(this.deletePostCalled, "it triggered the delete action"); + }, + }); + + componentTest(`delete post button - can't delete`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canDelete: false }); + }, + test(assert) { + assert.equal( + queryAll("button.delete").length, + 0, + `button is not displayed` + ); + }, + }); + + componentTest(`delete post button - can't delete, can't flag`, { template: '{{mount-widget widget="post" args=args}}', beforeEach() { this.set("args", { canDeleteTopic: false, - showFlagDelete: true, - canFlag: true, + showFlagDelete: false, + canFlag: false, }); }, - - async test(assert) { - await click(".show-more-actions"); - + test(assert) { + assert.equal( + queryAll("button.delete").length, + 0, + `delete button is not displayed` + ); assert.equal( queryAll("button.create-flag").length, - 1, - `button is displayed` - ); - assert.equal(queryAll("button.delete").length, 1, `button is displayed`); - assert.equal( - queryAll("button.delete").attr("title"), - I18n.t("post.controls.delete_topic_disallowed"), - `shows the right button title for users without permissions` + 0, + `flag button is not displayed` ); }, - } -); + }); -widgetTest("recover topic button", { - template: - '{{mount-widget widget="post" args=args recoverPost=(action "recoverPost")}}', - beforeEach() { - this.set("args", { canRecoverTopic: true }); - this.on("recoverPost", () => (this.recovered = true)); - }, - async test(assert) { - await click("button.recover"); - assert.ok(this.recovered); - }, -}); - -widgetTest(`recover topic button - can't recover`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canRecoverTopic: false }); - }, - test(assert) { - assert.equal( - queryAll("button.recover").length, - 0, - `button is not displayed` - ); - }, -}); - -widgetTest("delete post button", { - template: - '{{mount-widget widget="post" args=args deletePost=(action "deletePost")}}', - beforeEach() { - this.set("args", { canDelete: true, canFlag: true }); - this.on("deletePost", () => (this.deletePostCalled = true)); - }, - async test(assert) { - await click(".show-more-actions"); - await click("button.delete"); - assert.ok(this.deletePostCalled, "it triggered the delete action"); - }, -}); - -widgetTest(`delete post button - can't delete`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canDelete: false }); - }, - test(assert) { - assert.equal( - queryAll("button.delete").length, - 0, - `button is not displayed` - ); - }, -}); - -widgetTest(`delete post button - can't delete, can't flag`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - canDeleteTopic: false, - showFlagDelete: false, - canFlag: false, - }); - }, - test(assert) { - assert.equal( - queryAll("button.delete").length, - 0, - `delete button is not displayed` - ); - assert.equal( - queryAll("button.create-flag").length, - 0, - `flag button is not displayed` - ); - }, -}); - -widgetTest("recover post button", { - template: - '{{mount-widget widget="post" args=args recoverPost=(action "recoverPost")}}', - beforeEach() { - this.set("args", { canRecover: true }); - this.on("recoverPost", () => (this.recovered = true)); - }, - async test(assert) { - await click("button.recover"); - assert.ok(this.recovered); - }, -}); - -widgetTest(`recover post button - can't recover`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canRecover: false }); - }, - test(assert) { - assert.equal( - queryAll("button.recover").length, - 0, - `button is not displayed` - ); - }, -}); - -widgetTest(`flagging`, { - template: - '{{mount-widget widget="post" args=args showFlags=(action "showFlags")}}', - beforeEach() { - this.set("args", { canFlag: true }); - this.on("showFlags", () => (this.flagsShown = true)); - }, - async test(assert) { - assert.ok(queryAll("button.create-flag").length === 1); - - await click("button.create-flag"); - assert.ok(this.flagsShown, "it triggered the action"); - }, -}); - -widgetTest(`flagging: can't flag`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canFlag: false }); - }, - test(assert) { - assert.ok(queryAll("button.create-flag").length === 0); - }, -}); - -widgetTest(`flagging: can't flag when post is hidden`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canFlag: true, hidden: true }); - }, - test(assert) { - assert.ok(queryAll("button.create-flag").length === 0); - }, -}); - -widgetTest(`read indicator`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { read: true }); - }, - test(assert) { - assert.ok(queryAll(".read-state.read").length); - }, -}); - -widgetTest(`unread indicator`, { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { read: false }); - }, - test(assert) { - assert.ok(queryAll(".read-state").length); - }, -}); - -widgetTest("reply directly above (supressed)", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - replyToUsername: "eviltrout", - replyToAvatarTemplate: "/images/avatar.png", - replyDirectlyAbove: true, - }); - }, - test(assert) { - assert.equal(queryAll("a.reply-to-tab").length, 0, "hides the tab"); - assert.equal( - queryAll(".avoid-tab").length, - 0, - "doesn't have the avoid tab class" - ); - }, -}); - -widgetTest("reply a few posts above (supressed)", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - replyToUsername: "eviltrout", - replyToAvatarTemplate: "/images/avatar.png", - replyDirectlyAbove: false, - }); - }, - test(assert) { - assert.ok(queryAll("a.reply-to-tab").length, "shows the tab"); - assert.equal(queryAll(".avoid-tab").length, 1, "has the avoid tab class"); - }, -}); - -widgetTest("reply directly above", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - replyToUsername: "eviltrout", - replyToAvatarTemplate: "/images/avatar.png", - replyDirectlyAbove: true, - }); - this.siteSettings.suppress_reply_directly_above = false; - }, - async test(assert) { - assert.equal(queryAll(".avoid-tab").length, 1, "has the avoid tab class"); - await click("a.reply-to-tab"); - assert.equal(queryAll("section.embedded-posts.top .cooked").length, 1); - assert.equal(queryAll("section.embedded-posts .d-icon-arrow-up").length, 1); - }, -}); - -widgetTest("cooked content hidden", { - template: - '{{mount-widget widget="post" args=args expandHidden=(action "expandHidden")}}', - beforeEach() { - this.set("args", { cooked_hidden: true }); - this.on("expandHidden", () => (this.unhidden = true)); - }, - async test(assert) { - await click(".topic-body .expand-hidden"); - assert.ok(this.unhidden, "triggers the action"); - }, -}); - -widgetTest("expand first post", { - template: '{{mount-widget widget="post" model=post args=args}}', - beforeEach(store) { - this.set("args", { expandablePost: true }); - this.set("post", store.createRecord("post", { id: 1234 })); - }, - async test(assert) { - await click(".topic-body .expand-post"); - assert.equal(queryAll(".expand-post").length, 0, "button is gone"); - }, -}); - -widgetTest("can't bookmark", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canBookmark: false }); - }, - test(assert) { - assert.equal(queryAll("button.bookmark").length, 0); - assert.equal(queryAll("button.bookmarked").length, 0); - }, -}); - -widgetTest("bookmark", { - template: - '{{mount-widget widget="post" args=args toggleBookmark=(action "toggleBookmark")}}', - beforeEach() { - const args = { canBookmark: true }; - - this.set("args", args); - this.on("toggleBookmark", () => (args.bookmarked = true)); - }, - async test(assert) { - assert.equal(queryAll(".post-menu-area .bookmark").length, 1); - assert.equal(queryAll("button.bookmarked").length, 0); - }, -}); - -widgetTest("can't show admin menu when you can't manage", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canManage: false }); - }, - test(assert) { - assert.equal(queryAll(".post-menu-area .show-post-admin-menu").length, 0); - }, -}); - -widgetTest("show admin menu", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canManage: true }); - }, - async test(assert) { - assert.equal(queryAll(".post-admin-menu").length, 0); - await click(".post-menu-area .show-post-admin-menu"); - assert.equal(queryAll(".post-admin-menu").length, 1, "it shows the popup"); - await click(".post-menu-area"); - assert.equal( - queryAll(".post-admin-menu").length, - 0, - "clicking outside clears the popup" - ); - }, -}); - -widgetTest("toggle moderator post", { - template: - '{{mount-widget widget="post" args=args togglePostType=(action "togglePostType")}}', - beforeEach() { - this.currentUser.set("moderator", true); - this.set("args", { canManage: true }); - this.on("togglePostType", () => (this.toggled = true)); - }, - async test(assert) { - await click(".post-menu-area .show-post-admin-menu"); - await click(".post-admin-menu .toggle-post-type"); - - assert.ok(this.toggled); - assert.equal(queryAll(".post-admin-menu").length, 0, "also hides the menu"); - }, -}); -widgetTest("toggle moderator post", { - template: - '{{mount-widget widget="post" args=args togglePostType=(action "togglePostType")}}', - beforeEach() { - this.currentUser.set("moderator", true); - this.set("args", { canManage: true }); - this.on("togglePostType", () => (this.toggled = true)); - }, - async test(assert) { - await click(".post-menu-area .show-post-admin-menu"); - await click(".post-admin-menu .toggle-post-type"); - - assert.ok(this.toggled); - assert.equal(queryAll(".post-admin-menu").length, 0, "also hides the menu"); - }, -}); - -widgetTest("rebake post", { - template: - '{{mount-widget widget="post" args=args rebakePost=(action "rebakePost")}}', - beforeEach() { - this.set("args", { canManage: true }); - this.on("rebakePost", () => (this.baked = true)); - }, - async test(assert) { - await click(".post-menu-area .show-post-admin-menu"); - await click(".post-admin-menu .rebuild-html"); - assert.ok(this.baked); - assert.equal(queryAll(".post-admin-menu").length, 0, "also hides the menu"); - }, -}); - -widgetTest("unhide post", { - template: - '{{mount-widget widget="post" args=args unhidePost=(action "unhidePost")}}', - beforeEach() { - this.set("args", { canManage: true, hidden: true }); - this.on("unhidePost", () => (this.unhidden = true)); - }, - async test(assert) { - await click(".post-menu-area .show-post-admin-menu"); - await click(".post-admin-menu .unhide-post"); - assert.ok(this.unhidden); - assert.equal(queryAll(".post-admin-menu").length, 0, "also hides the menu"); - }, -}); - -widgetTest("change owner", { - template: - '{{mount-widget widget="post" args=args changePostOwner=(action "changePostOwner")}}', - beforeEach() { - this.currentUser.admin = true; - this.set("args", { canManage: true }); - this.on("changePostOwner", () => (this.owned = true)); - }, - async test(assert) { - await click(".post-menu-area .show-post-admin-menu"); - await click(".post-admin-menu .change-owner"); - assert.ok(this.owned); - assert.equal(queryAll(".post-admin-menu").length, 0, "also hides the menu"); - }, -}); - -widgetTest("reply", { - template: - '{{mount-widget widget="post" args=args replyToPost=(action "replyToPost")}}', - beforeEach() { - this.set("args", { canCreatePost: true }); - this.on("replyToPost", () => (this.replied = true)); - }, - async test(assert) { - await click(".post-controls .create"); - assert.ok(this.replied); - }, -}); - -widgetTest("reply - without permissions", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { canCreatePost: false }); - }, - test(assert) { - assert.equal(queryAll(".post-controls .create").length, 0); - }, -}); - -widgetTest("replies - no replies", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { replyCount: 0 }); - }, - test(assert) { - assert.equal(queryAll("button.show-replies").length, 0); - }, -}); - -widgetTest("replies - multiple replies", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.siteSettings.suppress_reply_directly_below = true; - this.set("args", { replyCount: 2, replyDirectlyBelow: true }); - }, - test(assert) { - assert.equal(queryAll("button.show-replies").length, 1); - }, -}); - -widgetTest("replies - one below, suppressed", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.siteSettings.suppress_reply_directly_below = true; - this.set("args", { replyCount: 1, replyDirectlyBelow: true }); - }, - test(assert) { - assert.equal(queryAll("button.show-replies").length, 0); - }, -}); - -widgetTest("replies - one below, not suppressed", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.siteSettings.suppress_reply_directly_below = false; - this.set("args", { id: 6654, replyCount: 1, replyDirectlyBelow: true }); - }, - async test(assert) { - await click("button.show-replies"); - assert.equal(queryAll("section.embedded-posts.bottom .cooked").length, 1); - assert.equal( - queryAll("section.embedded-posts .d-icon-arrow-down").length, - 1 - ); - }, -}); - -widgetTest("topic map not shown", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { showTopicMap: false }); - }, - test(assert) { - assert.equal(queryAll(".topic-map").length, 0); - }, -}); - -widgetTest("topic map - few posts", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - showTopicMap: true, - topicPostsCount: 2, - participants: [{ username: "eviltrout" }, { username: "codinghorror" }], - }); - }, - async test(assert) { - assert.equal( - queryAll("li.avatars a.poster").length, - 0, - "shows no participants when collapsed" - ); - - await click("nav.buttons button"); - assert.equal( - queryAll(".topic-map-expanded a.poster").length, - 2, - "shows all when expanded" - ); - }, -}); - -widgetTest("topic map - participants", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - showTopicMap: true, - topicPostsCount: 10, - participants: [ - { username: "eviltrout" }, - { username: "codinghorror" }, - { username: "sam" }, - { username: "ZogStrIP" }, - ], - userFilters: ["sam", "codinghorror"], - }); - }, - async test(assert) { - assert.equal( - queryAll("li.avatars a.poster").length, - 3, - "limits to three participants" - ); - - await click("nav.buttons button"); - assert.equal(queryAll("li.avatars a.poster").length, 0); - assert.equal( - queryAll(".topic-map-expanded a.poster").length, - 4, - "shows all when expanded" - ); - assert.equal(queryAll("a.poster.toggled").length, 2, "two are toggled"); - }, -}); - -widgetTest("topic map - links", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - showTopicMap: true, - topicLinks: [ - { url: "http://link1.example.com", clicks: 0 }, - { url: "http://link2.example.com", clicks: 0 }, - { url: "http://link3.example.com", clicks: 0 }, - { url: "http://link4.example.com", clicks: 0 }, - { url: "http://link5.example.com", clicks: 0 }, - { url: "http://link6.example.com", clicks: 0 }, - ], - }); - }, - async test(assert) { - assert.equal(queryAll(".topic-map").length, 1); - assert.equal(queryAll(".map.map-collapsed").length, 1); - assert.equal(queryAll(".topic-map-expanded").length, 0); - - await click("nav.buttons button"); - assert.equal(queryAll(".map.map-collapsed").length, 0); - assert.equal(queryAll(".topic-map .d-icon-chevron-up").length, 1); - assert.equal(queryAll(".topic-map-expanded").length, 1); - assert.equal( - queryAll(".topic-map-expanded .topic-link").length, - 5, - "it limits the links displayed" - ); - - await click(".link-summary button"); - assert.equal( - queryAll(".topic-map-expanded .topic-link").length, - 6, - "all links now shown" - ); - }, -}); - -widgetTest("topic map - no summary", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { showTopicMap: true }); - }, - test(assert) { - assert.equal(queryAll(".toggle-summary").length, 0); - }, -}); - -widgetTest("topic map - has summary", { - template: - '{{mount-widget widget="post" args=args toggleSummary=(action "toggleSummary")}}', - beforeEach() { - this.set("args", { showTopicMap: true, hasTopicSummary: true }); - this.on("toggleSummary", () => (this.summaryToggled = true)); - }, - async test(assert) { - assert.equal(queryAll(".toggle-summary").length, 1); - - await click(".toggle-summary button"); - assert.ok(this.summaryToggled); - }, -}); - -widgetTest("pm map", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - showTopicMap: true, - showPMMap: true, - allowedGroups: [], - allowedUsers: [EmberObject.create({ username: "eviltrout" })], - }); - }, - test(assert) { - assert.equal(queryAll(".private-message-map").length, 1); - assert.equal(queryAll(".private-message-map .user").length, 1); - }, -}); - -widgetTest("post notice - with username", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - const twoDaysAgo = new Date(); - twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); - this.siteSettings.display_name_on_posts = false; - this.siteSettings.prioritize_username_in_ux = true; - this.siteSettings.old_post_notice_days = 14; - this.set("args", { - username: "codinghorror", - name: "Jeff", - created_at: new Date(), - notice: { - type: "returning_user", - lastPostedAt: twoDaysAgo, - }, - }); - }, - test(assert) { - assert.equal( - queryAll(".post-notice.returning-user:not(.old)").text().trim(), - I18n.t("post.notice.returning_user", { - user: "codinghorror", - time: "2 days ago", - }) - ); - }, -}); - -widgetTest("post notice - with name", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.siteSettings.display_name_on_posts = true; - this.siteSettings.prioritize_username_in_ux = false; - this.siteSettings.old_post_notice_days = 14; - this.set("args", { - username: "codinghorror", - name: "Jeff", - created_at: new Date(2019, 0, 1), - notice: { type: "new_user" }, - }); - }, - test(assert) { - assert.equal( - queryAll(".post-notice.old.new-user").text().trim(), - I18n.t("post.notice.new_user", { user: "Jeff", time: "Jan '10" }) - ); - }, -}); - -widgetTest("show group request in post", { - template: '{{mount-widget widget="post" args=args}}', - beforeEach() { - this.set("args", { - username: "foo", - requestedGroupName: "testGroup", - }); - }, - test(assert) { - const link = queryAll(".group-request a"); - assert.equal(link.text().trim(), I18n.t("groups.requests.handle")); - assert.equal(link.attr("href"), "/g/testGroup/requests?filter=foo"); - }, + componentTest("recover post button", { + template: + '{{mount-widget widget="post" args=args recoverPost=(action "recoverPost")}}', + beforeEach() { + this.set("args", { canRecover: true }); + this.on("recoverPost", () => (this.recovered = true)); + }, + async test(assert) { + await click("button.recover"); + assert.ok(this.recovered); + }, + }); + + componentTest(`recover post button - can't recover`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canRecover: false }); + }, + test(assert) { + assert.equal( + queryAll("button.recover").length, + 0, + `button is not displayed` + ); + }, + }); + + componentTest(`flagging`, { + template: + '{{mount-widget widget="post" args=args showFlags=(action "showFlags")}}', + beforeEach() { + this.set("args", { canFlag: true }); + this.on("showFlags", () => (this.flagsShown = true)); + }, + async test(assert) { + assert.ok(queryAll("button.create-flag").length === 1); + + await click("button.create-flag"); + assert.ok(this.flagsShown, "it triggered the action"); + }, + }); + + componentTest(`flagging: can't flag`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canFlag: false }); + }, + test(assert) { + assert.ok(queryAll("button.create-flag").length === 0); + }, + }); + + componentTest(`flagging: can't flag when post is hidden`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canFlag: true, hidden: true }); + }, + test(assert) { + assert.ok(queryAll("button.create-flag").length === 0); + }, + }); + + componentTest(`read indicator`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { read: true }); + }, + test(assert) { + assert.ok(queryAll(".read-state.read").length); + }, + }); + + componentTest(`unread indicator`, { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { read: false }); + }, + test(assert) { + assert.ok(queryAll(".read-state").length); + }, + }); + + componentTest("reply directly above (supressed)", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + replyToUsername: "eviltrout", + replyToAvatarTemplate: "/images/avatar.png", + replyDirectlyAbove: true, + }); + }, + test(assert) { + assert.equal(queryAll("a.reply-to-tab").length, 0, "hides the tab"); + assert.equal( + queryAll(".avoid-tab").length, + 0, + "doesn't have the avoid tab class" + ); + }, + }); + + componentTest("reply a few posts above (supressed)", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + replyToUsername: "eviltrout", + replyToAvatarTemplate: "/images/avatar.png", + replyDirectlyAbove: false, + }); + }, + test(assert) { + assert.ok(queryAll("a.reply-to-tab").length, "shows the tab"); + assert.equal(queryAll(".avoid-tab").length, 1, "has the avoid tab class"); + }, + }); + + componentTest("reply directly above", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + replyToUsername: "eviltrout", + replyToAvatarTemplate: "/images/avatar.png", + replyDirectlyAbove: true, + }); + this.siteSettings.suppress_reply_directly_above = false; + }, + async test(assert) { + assert.equal(queryAll(".avoid-tab").length, 1, "has the avoid tab class"); + await click("a.reply-to-tab"); + assert.equal(queryAll("section.embedded-posts.top .cooked").length, 1); + assert.equal( + queryAll("section.embedded-posts .d-icon-arrow-up").length, + 1 + ); + }, + }); + + componentTest("cooked content hidden", { + template: + '{{mount-widget widget="post" args=args expandHidden=(action "expandHidden")}}', + beforeEach() { + this.set("args", { cooked_hidden: true }); + this.on("expandHidden", () => (this.unhidden = true)); + }, + async test(assert) { + await click(".topic-body .expand-hidden"); + assert.ok(this.unhidden, "triggers the action"); + }, + }); + + componentTest("expand first post", { + template: '{{mount-widget widget="post" model=post args=args}}', + beforeEach(store) { + this.set("args", { expandablePost: true }); + this.set("post", store.createRecord("post", { id: 1234 })); + }, + async test(assert) { + await click(".topic-body .expand-post"); + assert.equal(queryAll(".expand-post").length, 0, "button is gone"); + }, + }); + + componentTest("can't bookmark", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canBookmark: false }); + }, + test(assert) { + assert.equal(queryAll("button.bookmark").length, 0); + assert.equal(queryAll("button.bookmarked").length, 0); + }, + }); + + componentTest("bookmark", { + template: + '{{mount-widget widget="post" args=args toggleBookmark=(action "toggleBookmark")}}', + beforeEach() { + const args = { canBookmark: true }; + + this.set("args", args); + this.on("toggleBookmark", () => (args.bookmarked = true)); + }, + async test(assert) { + assert.equal(queryAll(".post-menu-area .bookmark").length, 1); + assert.equal(queryAll("button.bookmarked").length, 0); + }, + }); + + componentTest("can't show admin menu when you can't manage", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canManage: false }); + }, + test(assert) { + assert.equal(queryAll(".post-menu-area .show-post-admin-menu").length, 0); + }, + }); + + componentTest("show admin menu", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canManage: true }); + }, + async test(assert) { + assert.equal(queryAll(".post-admin-menu").length, 0); + await click(".post-menu-area .show-post-admin-menu"); + assert.equal( + queryAll(".post-admin-menu").length, + 1, + "it shows the popup" + ); + await click(".post-menu-area"); + assert.equal( + queryAll(".post-admin-menu").length, + 0, + "clicking outside clears the popup" + ); + }, + }); + + componentTest("toggle moderator post", { + template: + '{{mount-widget widget="post" args=args togglePostType=(action "togglePostType")}}', + beforeEach() { + this.currentUser.set("moderator", true); + this.set("args", { canManage: true }); + this.on("togglePostType", () => (this.toggled = true)); + }, + async test(assert) { + await click(".post-menu-area .show-post-admin-menu"); + await click(".post-admin-menu .toggle-post-type"); + + assert.ok(this.toggled); + assert.equal( + queryAll(".post-admin-menu").length, + 0, + "also hides the menu" + ); + }, + }); + componentTest("toggle moderator post", { + template: + '{{mount-widget widget="post" args=args togglePostType=(action "togglePostType")}}', + beforeEach() { + this.currentUser.set("moderator", true); + this.set("args", { canManage: true }); + this.on("togglePostType", () => (this.toggled = true)); + }, + async test(assert) { + await click(".post-menu-area .show-post-admin-menu"); + await click(".post-admin-menu .toggle-post-type"); + + assert.ok(this.toggled); + assert.equal( + queryAll(".post-admin-menu").length, + 0, + "also hides the menu" + ); + }, + }); + + componentTest("rebake post", { + template: + '{{mount-widget widget="post" args=args rebakePost=(action "rebakePost")}}', + beforeEach() { + this.set("args", { canManage: true }); + this.on("rebakePost", () => (this.baked = true)); + }, + async test(assert) { + await click(".post-menu-area .show-post-admin-menu"); + await click(".post-admin-menu .rebuild-html"); + assert.ok(this.baked); + assert.equal( + queryAll(".post-admin-menu").length, + 0, + "also hides the menu" + ); + }, + }); + + componentTest("unhide post", { + template: + '{{mount-widget widget="post" args=args unhidePost=(action "unhidePost")}}', + beforeEach() { + this.set("args", { canManage: true, hidden: true }); + this.on("unhidePost", () => (this.unhidden = true)); + }, + async test(assert) { + await click(".post-menu-area .show-post-admin-menu"); + await click(".post-admin-menu .unhide-post"); + assert.ok(this.unhidden); + assert.equal( + queryAll(".post-admin-menu").length, + 0, + "also hides the menu" + ); + }, + }); + + componentTest("change owner", { + template: + '{{mount-widget widget="post" args=args changePostOwner=(action "changePostOwner")}}', + beforeEach() { + this.currentUser.admin = true; + this.set("args", { canManage: true }); + this.on("changePostOwner", () => (this.owned = true)); + }, + async test(assert) { + await click(".post-menu-area .show-post-admin-menu"); + await click(".post-admin-menu .change-owner"); + assert.ok(this.owned); + assert.equal( + queryAll(".post-admin-menu").length, + 0, + "also hides the menu" + ); + }, + }); + + componentTest("reply", { + template: + '{{mount-widget widget="post" args=args replyToPost=(action "replyToPost")}}', + beforeEach() { + this.set("args", { canCreatePost: true }); + this.on("replyToPost", () => (this.replied = true)); + }, + async test(assert) { + await click(".post-controls .create"); + assert.ok(this.replied); + }, + }); + + componentTest("reply - without permissions", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { canCreatePost: false }); + }, + test(assert) { + assert.equal(queryAll(".post-controls .create").length, 0); + }, + }); + + componentTest("replies - no replies", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { replyCount: 0 }); + }, + test(assert) { + assert.equal(queryAll("button.show-replies").length, 0); + }, + }); + + componentTest("replies - multiple replies", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.siteSettings.suppress_reply_directly_below = true; + this.set("args", { replyCount: 2, replyDirectlyBelow: true }); + }, + test(assert) { + assert.equal(queryAll("button.show-replies").length, 1); + }, + }); + + componentTest("replies - one below, suppressed", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.siteSettings.suppress_reply_directly_below = true; + this.set("args", { replyCount: 1, replyDirectlyBelow: true }); + }, + test(assert) { + assert.equal(queryAll("button.show-replies").length, 0); + }, + }); + + componentTest("replies - one below, not suppressed", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.siteSettings.suppress_reply_directly_below = false; + this.set("args", { id: 6654, replyCount: 1, replyDirectlyBelow: true }); + }, + async test(assert) { + await click("button.show-replies"); + assert.equal(queryAll("section.embedded-posts.bottom .cooked").length, 1); + assert.equal( + queryAll("section.embedded-posts .d-icon-arrow-down").length, + 1 + ); + }, + }); + + componentTest("topic map not shown", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { showTopicMap: false }); + }, + test(assert) { + assert.equal(queryAll(".topic-map").length, 0); + }, + }); + + componentTest("topic map - few posts", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + showTopicMap: true, + topicPostsCount: 2, + participants: [{ username: "eviltrout" }, { username: "codinghorror" }], + }); + }, + async test(assert) { + assert.equal( + queryAll("li.avatars a.poster").length, + 0, + "shows no participants when collapsed" + ); + + await click("nav.buttons button"); + assert.equal( + queryAll(".topic-map-expanded a.poster").length, + 2, + "shows all when expanded" + ); + }, + }); + + componentTest("topic map - participants", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + showTopicMap: true, + topicPostsCount: 10, + participants: [ + { username: "eviltrout" }, + { username: "codinghorror" }, + { username: "sam" }, + { username: "ZogStrIP" }, + ], + userFilters: ["sam", "codinghorror"], + }); + }, + async test(assert) { + assert.equal( + queryAll("li.avatars a.poster").length, + 3, + "limits to three participants" + ); + + await click("nav.buttons button"); + assert.equal(queryAll("li.avatars a.poster").length, 0); + assert.equal( + queryAll(".topic-map-expanded a.poster").length, + 4, + "shows all when expanded" + ); + assert.equal(queryAll("a.poster.toggled").length, 2, "two are toggled"); + }, + }); + + componentTest("topic map - links", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + showTopicMap: true, + topicLinks: [ + { url: "http://link1.example.com", clicks: 0 }, + { url: "http://link2.example.com", clicks: 0 }, + { url: "http://link3.example.com", clicks: 0 }, + { url: "http://link4.example.com", clicks: 0 }, + { url: "http://link5.example.com", clicks: 0 }, + { url: "http://link6.example.com", clicks: 0 }, + ], + }); + }, + async test(assert) { + assert.equal(queryAll(".topic-map").length, 1); + assert.equal(queryAll(".map.map-collapsed").length, 1); + assert.equal(queryAll(".topic-map-expanded").length, 0); + + await click("nav.buttons button"); + assert.equal(queryAll(".map.map-collapsed").length, 0); + assert.equal(queryAll(".topic-map .d-icon-chevron-up").length, 1); + assert.equal(queryAll(".topic-map-expanded").length, 1); + assert.equal( + queryAll(".topic-map-expanded .topic-link").length, + 5, + "it limits the links displayed" + ); + + await click(".link-summary button"); + assert.equal( + queryAll(".topic-map-expanded .topic-link").length, + 6, + "all links now shown" + ); + }, + }); + + componentTest("topic map - no summary", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { showTopicMap: true }); + }, + test(assert) { + assert.equal(queryAll(".toggle-summary").length, 0); + }, + }); + + componentTest("topic map - has summary", { + template: + '{{mount-widget widget="post" args=args toggleSummary=(action "toggleSummary")}}', + beforeEach() { + this.set("args", { showTopicMap: true, hasTopicSummary: true }); + this.on("toggleSummary", () => (this.summaryToggled = true)); + }, + async test(assert) { + assert.equal(queryAll(".toggle-summary").length, 1); + + await click(".toggle-summary button"); + assert.ok(this.summaryToggled); + }, + }); + + componentTest("pm map", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + showTopicMap: true, + showPMMap: true, + allowedGroups: [], + allowedUsers: [EmberObject.create({ username: "eviltrout" })], + }); + }, + test(assert) { + assert.equal(queryAll(".private-message-map").length, 1); + assert.equal(queryAll(".private-message-map .user").length, 1); + }, + }); + + componentTest("post notice - with username", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + const twoDaysAgo = new Date(); + twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); + this.siteSettings.display_name_on_posts = false; + this.siteSettings.prioritize_username_in_ux = true; + this.siteSettings.old_post_notice_days = 14; + this.set("args", { + username: "codinghorror", + name: "Jeff", + created_at: new Date(), + notice: { + type: "returning_user", + lastPostedAt: twoDaysAgo, + }, + }); + }, + test(assert) { + assert.equal( + queryAll(".post-notice.returning-user:not(.old)").text().trim(), + I18n.t("post.notice.returning_user", { + user: "codinghorror", + time: "2 days ago", + }) + ); + }, + }); + + componentTest("post notice - with name", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.siteSettings.display_name_on_posts = true; + this.siteSettings.prioritize_username_in_ux = false; + this.siteSettings.old_post_notice_days = 14; + this.set("args", { + username: "codinghorror", + name: "Jeff", + created_at: new Date(2019, 0, 1), + notice: { type: "new_user" }, + }); + }, + test(assert) { + assert.equal( + queryAll(".post-notice.old.new-user").text().trim(), + I18n.t("post.notice.new_user", { user: "Jeff", time: "Jan '10" }) + ); + }, + }); + + componentTest("show group request in post", { + template: '{{mount-widget widget="post" args=args}}', + beforeEach() { + this.set("args", { + username: "foo", + requestedGroupName: "testGroup", + }); + }, + test(assert) { + const link = queryAll(".group-request a"); + assert.equal(link.text().trim(), I18n.t("groups.requests.handle")); + assert.equal(link.attr("href"), "/g/testGroup/requests?filter=foo"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/poster-name-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/poster-name-test.js index e94307eb8a..4dbed1a782 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/poster-name-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/poster-name-test.js @@ -1,73 +1,79 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForWidget("poster-name"); +discourseModule("Integration | Component | Widget | poster-name", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("basic rendering", { - template: '{{mount-widget widget="poster-name" args=args}}', - beforeEach() { - this.set("args", { - username: "eviltrout", - usernameUrl: "/u/eviltrout", - name: "Robin Ward", - user_title: "Trout Master", - }); - }, - test(assert) { - assert.ok(queryAll(".names").length); - assert.ok(queryAll("span.username").length); - assert.ok(queryAll("a[data-user-card=eviltrout]").length); - assert.equal(queryAll(".username a").text(), "eviltrout"); - assert.equal(queryAll(".full-name a").text(), "Robin Ward"); - assert.equal(queryAll(".user-title").text(), "Trout Master"); - }, -}); - -widgetTest("extra classes and glyphs", { - template: '{{mount-widget widget="poster-name" args=args}}', - beforeEach() { - this.set("args", { - username: "eviltrout", - usernameUrl: "/u/eviltrout", - staff: true, - admin: true, - moderator: true, - new_user: true, - primary_group_name: "fish", - }); - }, - test(assert) { - assert.ok(queryAll("span.staff").length); - assert.ok(queryAll("span.admin").length); - assert.ok(queryAll("span.moderator").length); - assert.ok(queryAll(".d-icon-shield-alt").length); - assert.ok(queryAll("span.new-user").length); - assert.ok(queryAll("span.fish").length); - }, -}); - -widgetTest("disable display name on posts", { - template: '{{mount-widget widget="poster-name" args=args}}', - beforeEach() { - this.siteSettings.display_name_on_posts = false; - this.set("args", { username: "eviltrout", name: "Robin Ward" }); - }, - test(assert) { - assert.equal(queryAll(".full-name").length, 0); - }, -}); - -widgetTest("doesn't render a name if it's similar to the username", { - template: '{{mount-widget widget="poster-name" args=args}}', - beforeEach() { - this.siteSettings.prioritize_username_in_ux = true; - this.siteSettings.display_name_on_posts = true; - this.set("args", { username: "eviltrout", name: "evil-trout" }); - }, - test(assert) { - assert.equal(queryAll(".second").length, 0); - }, + componentTest("basic rendering", { + template: '{{mount-widget widget="poster-name" args=args}}', + beforeEach() { + this.set("args", { + username: "eviltrout", + usernameUrl: "/u/eviltrout", + name: "Robin Ward", + user_title: "Trout Master", + }); + }, + test(assert) { + assert.ok(queryAll(".names").length); + assert.ok(queryAll("span.username").length); + assert.ok(queryAll("a[data-user-card=eviltrout]").length); + assert.equal(queryAll(".username a").text(), "eviltrout"); + assert.equal(queryAll(".full-name a").text(), "Robin Ward"); + assert.equal(queryAll(".user-title").text(), "Trout Master"); + }, + }); + + componentTest("extra classes and glyphs", { + template: '{{mount-widget widget="poster-name" args=args}}', + beforeEach() { + this.set("args", { + username: "eviltrout", + usernameUrl: "/u/eviltrout", + staff: true, + admin: true, + moderator: true, + new_user: true, + primary_group_name: "fish", + }); + }, + test(assert) { + assert.ok(queryAll("span.staff").length); + assert.ok(queryAll("span.admin").length); + assert.ok(queryAll("span.moderator").length); + assert.ok(queryAll(".d-icon-shield-alt").length); + assert.ok(queryAll("span.new-user").length); + assert.ok(queryAll("span.fish").length); + }, + }); + + componentTest("disable display name on posts", { + template: '{{mount-widget widget="poster-name" args=args}}', + beforeEach() { + this.siteSettings.display_name_on_posts = false; + this.set("args", { username: "eviltrout", name: "Robin Ward" }); + }, + test(assert) { + assert.equal(queryAll(".full-name").length, 0); + }, + }); + + componentTest("doesn't render a name if it's similar to the username", { + template: '{{mount-widget widget="poster-name" args=args}}', + beforeEach() { + this.siteSettings.prioritize_username_in_ux = true; + this.siteSettings.display_name_on_posts = true; + this.set("args", { username: "eviltrout", name: "evil-trout" }); + }, + test(assert) { + assert.equal(queryAll(".second").length, 0); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/quick-access-item-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/quick-access-item-test.js index e8bba847b8..5834b2cb27 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/quick-access-item-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/quick-access-item-test.js @@ -1,35 +1,42 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; - -moduleForWidget("quick-access-item"); + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; const CONTENT_DIV_SELECTOR = "li > a > div"; -widgetTest("content attribute is escaped", { - template: '{{mount-widget widget="quick-access-item" args=args}}', +discourseModule( + "Integration | Component | Widget | quick-access-item", + function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.set("args", { content: "bold" }); - }, + componentTest("content attribute is escaped", { + template: '{{mount-widget widget="quick-access-item" args=args}}', - test(assert) { - const contentDiv = queryAll(CONTENT_DIV_SELECTOR)[0]; - assert.equal(contentDiv.innerText, "bold"); - }, -}); + beforeEach() { + this.set("args", { content: "bold" }); + }, -widgetTest("escapedContent attribute is not escaped", { - template: '{{mount-widget widget="quick-access-item" args=args}}', + test(assert) { + const contentDiv = queryAll(CONTENT_DIV_SELECTOR)[0]; + assert.equal(contentDiv.innerText, "bold"); + }, + }); - beforeEach() { - this.set("args", { escapedContent: ""quote"" }); - }, + componentTest("escapedContent attribute is not escaped", { + template: '{{mount-widget widget="quick-access-item" args=args}}', - test(assert) { - const contentDiv = queryAll(CONTENT_DIV_SELECTOR)[0]; - assert.equal(contentDiv.innerText, '"quote"'); - }, -}); + beforeEach() { + this.set("args", { escapedContent: ""quote"" }); + }, + + test(assert) { + const contentDiv = queryAll(CONTENT_DIV_SELECTOR)[0]; + assert.equal(contentDiv.innerText, '"quote"'); + }, + }); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/small-user-list-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/small-user-list-test.js index 460ca12705..46e3e12190 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/small-user-list-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/small-user-list-test.js @@ -1,24 +1,30 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForWidget("small-user-list"); +discourseModule("Integration | Component | Widget | small-user-list", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("renders avatars and support for unknown", { - template: '{{mount-widget widget="small-user-list" args=args}}', - beforeEach() { - this.set("args", { - users: [ - { id: 456, username: "eviltrout" }, - { id: 457, username: "someone", unknown: true }, - ], - }); - }, - async test(assert) { - assert.ok(queryAll("[data-user-card=eviltrout]").length === 1); - assert.ok(queryAll("[data-user-card=someone]").length === 0); - assert.ok(queryAll(".unknown").length, "includes unkown user"); - }, + componentTest("renders avatars and support for unknown", { + template: '{{mount-widget widget="small-user-list" args=args}}', + beforeEach() { + this.set("args", { + users: [ + { id: 456, username: "eviltrout" }, + { id: 457, username: "someone", unknown: true }, + ], + }); + }, + async test(assert) { + assert.ok(queryAll("[data-user-card=eviltrout]").length === 1); + assert.ok(queryAll("[data-user-card=someone]").length === 0); + assert.ok(queryAll(".unknown").length, "includes unkown user"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/topic-admin-menu-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/topic-admin-menu-test.js index a3c1b3b29c..098cce1149 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/topic-admin-menu-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/topic-admin-menu-test.js @@ -1,13 +1,10 @@ -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; +import { exists, discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import Topic from "discourse/models/topic"; import Category from "discourse/models/category"; -moduleForWidget("topic-admin-menu-button"); - const createArgs = (topic) => { return { topic: topic, @@ -27,45 +24,58 @@ const createArgs = (topic) => { }; }; -widgetTest("topic-admin-menu-button is present for admin/moderators", { - template: '{{mount-widget widget="topic-admin-menu-button" args=args}}', +discourseModule( + "Integration | Component | Widget | topic-admin-menu-button", + function (hooks) { + setupRenderingTest(hooks); - beforeEach() { - this.currentUser.setProperties({ - admin: true, - moderator: true, - id: 123, + componentTest("topic-admin-menu-button is present for admin/moderators", { + template: '{{mount-widget widget="topic-admin-menu-button" args=args}}', + + beforeEach() { + this.currentUser.setProperties({ + admin: true, + moderator: true, + id: 123, + }); + const topic = Topic.create({ user_id: this.currentUser.id }); + topic.set("category_id", Category.create({ read_restricted: true }).id); + this.siteSettings.allow_featured_topic_on_user_profiles = true; + this.set("args", createArgs(topic)); + }, + + test(assert) { + assert.ok(exists(".toggle-admin-menu"), "admin wrench is present"); + }, }); - const topic = Topic.create({ user_id: this.currentUser.id }); - topic.set("category_id", Category.create({ read_restricted: true }).id); - this.siteSettings.allow_featured_topic_on_user_profiles = true; - this.set("args", createArgs(topic)); - }, - test(assert) { - assert.ok(exists(".toggle-admin-menu"), "admin wrench is present"); - }, -}); + componentTest( + "topic-admin-menu-button hides for non-admin when there is no action", + { + template: '{{mount-widget widget="topic-admin-menu-button" args=args}}', -widgetTest( - "topic-admin-menu-button hides for non-admin when there is no action", - { - template: '{{mount-widget widget="topic-admin-menu-button" args=args}}', + beforeEach() { + this.currentUser.setProperties({ + admin: false, + moderator: false, + id: 123, + }); + const topic = Topic.create({ user_id: this.currentUser.id }); + topic.set( + "category_id", + Category.create({ read_restricted: true }).id + ); + this.siteSettings.allow_featured_topic_on_user_profiles = true; + this.set("args", createArgs(topic)); + }, - beforeEach() { - this.currentUser.setProperties({ - admin: false, - moderator: false, - id: 123, - }); - const topic = Topic.create({ user_id: this.currentUser.id }); - topic.set("category_id", Category.create({ read_restricted: true }).id); - this.siteSettings.allow_featured_topic_on_user_profiles = true; - this.set("args", createArgs(topic)); - }, - - test(assert) { - assert.ok(!exists(".toggle-admin-menu"), "admin wrench is not present"); - }, + test(assert) { + assert.ok( + !exists(".toggle-admin-menu"), + "admin wrench is not present" + ); + }, + } + ); } ); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/topic-participant-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/topic-participant-test.js index b39a1ae821..c81534f3e5 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/topic-participant-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/topic-participant-test.js @@ -1,53 +1,60 @@ -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; +import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; -moduleForWidget("topic-participant"); +discourseModule( + "Integration | Component | Widget | topic-participant", + function (hooks) { + setupRenderingTest(hooks); -widgetTest("one post", { - template: '{{mount-widget widget="topic-participant" args=args}}', + componentTest("one post", { + template: '{{mount-widget widget="topic-participant" args=args}}', - beforeEach() { - this.set("args", { - username: "test", - avatar_template: "/images/avatar.png", - post_count: 1, + beforeEach() { + this.set("args", { + username: "test", + avatar_template: "/images/avatar.png", + post_count: 1, + }); + }, + + test(assert) { + assert.ok(exists("a.poster.trigger-user-card")); + assert.ok( + !exists("span.post-count"), + "don't show count for only 1 post" + ); + assert.ok(!exists(".avatar-flair"), "no avatar flair"); + }, }); - }, - test(assert) { - assert.ok(exists("a.poster.trigger-user-card")); - assert.ok(!exists("span.post-count"), "don't show count for only 1 post"); - assert.ok(!exists(".avatar-flair"), "no avatar flair"); - }, -}); + componentTest("many posts, a primary group with flair", { + template: '{{mount-widget widget="topic-participant" args=args}}', -widgetTest("many posts, a primary group with flair", { - template: '{{mount-widget widget="topic-participant" args=args}}', + beforeEach() { + this.set("args", { + username: "test", + avatar_template: "/images/avatar.png", + post_count: 5, + primary_group_name: "devs", + primary_group_flair_url: "/images/d-logo-sketch-small.png", + primary_group_flair_bg_color: "222", + }); + }, - beforeEach() { - this.set("args", { - username: "test", - avatar_template: "/images/avatar.png", - post_count: 5, - primary_group_name: "devs", - primary_group_flair_url: "/images/d-logo-sketch-small.png", - primary_group_flair_bg_color: "222", + test(assert) { + assert.ok(exists("a.poster.trigger-user-card")); + assert.ok(exists("span.post-count"), "show count for many posts"); + assert.ok( + exists(".group-devs a.poster"), + "add class for the group outside the link" + ); + assert.ok( + exists(".avatar-flair.avatar-flair-devs"), + "show flair with group class" + ); + }, }); - }, - - test(assert) { - assert.ok(exists("a.poster.trigger-user-card")); - assert.ok(exists("span.post-count"), "show count for many posts"); - assert.ok( - exists(".group-devs a.poster"), - "add class for the group outside the link" - ); - assert.ok( - exists(".avatar-flair.avatar-flair-devs"), - "show flair with group class" - ); - }, -}); + } +); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/topic-status-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/topic-status-test.js index 6bd000d520..6fce46e765 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/topic-status-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/topic-status-test.js @@ -1,41 +1,47 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import TopicStatusIcons from "discourse/helpers/topic-status-icons"; -moduleForWidget("topic-status"); +discourseModule("Integration | Component | Widget | topic-status", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("basics", { - template: '{{mount-widget widget="topic-status" args=args}}', - beforeEach(store) { - this.set("args", { - topic: store.createRecord("topic", { closed: true }), - disableActions: true, - }); - }, - test(assert) { - assert.ok(queryAll(".topic-status .d-icon-lock").length); - }, -}); + componentTest("basics", { + template: '{{mount-widget widget="topic-status" args=args}}', + beforeEach(store) { + this.set("args", { + topic: store.createRecord("topic", { closed: true }), + disableActions: true, + }); + }, + test(assert) { + assert.ok(queryAll(".topic-status .d-icon-lock").length); + }, + }); -widgetTest("extendability", { - template: '{{mount-widget widget="topic-status" args=args}}', - beforeEach(store) { - TopicStatusIcons.addObject([ - "has_accepted_answer", - "far-check-square", - "solved", - ]); - this.set("args", { - topic: store.createRecord("topic", { - has_accepted_answer: true, - }), - disableActions: true, - }); - }, - test(assert) { - assert.ok(queryAll(".topic-status .d-icon-far-check-square").length); - }, + componentTest("extendability", { + template: '{{mount-widget widget="topic-status" args=args}}', + beforeEach(store) { + TopicStatusIcons.addObject([ + "has_accepted_answer", + "far-check-square", + "solved", + ]); + this.set("args", { + topic: store.createRecord("topic", { + has_accepted_answer: true, + }), + disableActions: true, + }); + }, + test(assert) { + assert.ok(queryAll(".topic-status .d-icon-far-check-square").length); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/user-menu-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/user-menu-test.js index 756525b0cd..b9f0166214 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/user-menu-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/user-menu-test.js @@ -1,226 +1,232 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import sinon from "sinon"; import I18n from "I18n"; import DiscourseURL from "discourse/lib/url"; -import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; import { click } from "@ember/test-helpers"; -moduleForWidget("user-menu"); +discourseModule("Integration | Component | Widget | user-menu", function ( + hooks +) { + setupRenderingTest(hooks); -widgetTest("basics", { - template: '{{mount-widget widget="user-menu"}}', + componentTest("basics", { + template: '{{mount-widget widget="user-menu"}}', - test(assert) { - assert.ok(queryAll(".user-menu").length); - assert.ok(queryAll(".user-preferences-link").length); - assert.ok(queryAll(".user-notifications-link").length); - assert.ok(queryAll(".user-bookmarks-link").length); - assert.ok(queryAll(".quick-access-panel").length); - assert.ok(queryAll(".notifications-dismiss").length); - }, -}); + test(assert) { + assert.ok(queryAll(".user-menu").length); + assert.ok(queryAll(".user-preferences-link").length); + assert.ok(queryAll(".user-notifications-link").length); + assert.ok(queryAll(".user-bookmarks-link").length); + assert.ok(queryAll(".quick-access-panel").length); + assert.ok(queryAll(".notifications-dismiss").length); + }, + }); -widgetTest("notifications", { - template: '{{mount-widget widget="user-menu"}}', + componentTest("notifications", { + template: '{{mount-widget widget="user-menu"}}', - async test(assert) { - const $links = queryAll(".quick-access-panel li a"); + async test(assert) { + const $links = queryAll(".quick-access-panel li a"); - assert.equal($links.length, 5); - assert.ok($links[0].href.includes("/t/a-slug/123")); + assert.equal($links.length, 5); + assert.ok($links[0].href.includes("/t/a-slug/123")); - assert.ok( - $links[1].href.includes( - "/u/eviltrout/notifications/likes-received?acting_username=aquaman" - ) - ); + assert.ok( + $links[1].href.includes( + "/u/eviltrout/notifications/likes-received?acting_username=aquaman" + ) + ); - assert.equal( - $links[1].text, - `aquaman ${I18n.t("notifications.liked_consolidated_description", { - count: 5, - })}` - ); - - assert.ok($links[2].href.includes("/u/test2/messages/group/test")); - assert.ok( - $links[2].innerHTML.includes( - I18n.t("notifications.group_message_summary", { + assert.equal( + $links[1].text, + `aquaman ${I18n.t("notifications.liked_consolidated_description", { count: 5, - group_name: "test", - }) - ) - ); + })}` + ); - assert.ok($links[3].href.includes("/u/test1")); - assert.ok( - $links[3].innerHTML.includes( - I18n.t("notifications.invitee_accepted", { username: "test1" }) - ) - ); + assert.ok($links[2].href.includes("/u/test2/messages/group/test")); + assert.ok( + $links[2].innerHTML.includes( + I18n.t("notifications.group_message_summary", { + count: 5, + group_name: "test", + }) + ) + ); - assert.ok($links[4].href.includes("/g/test")); - assert.ok( - $links[4].innerHTML.includes( - I18n.t("notifications.membership_request_accepted", { - group_name: "test", - }) - ) - ); + assert.ok($links[3].href.includes("/u/test1")); + assert.ok( + $links[3].innerHTML.includes( + I18n.t("notifications.invitee_accepted", { username: "test1" }) + ) + ); - const routeToStub = sinon.stub(DiscourseURL, "routeTo"); - await click(".user-notifications-link"); - assert.ok( - routeToStub.calledWith(queryAll(".user-notifications-link")[0].href), - "a second click should redirect to the full notifications page" - ); - }, -}); - -widgetTest("log out", { - template: '{{mount-widget widget="user-menu" logout=(action "logout")}}', - - beforeEach() { - this.on("logout", () => (this.loggedOut = true)); - }, - - async test(assert) { - await click(".user-preferences-link"); - assert.ok(queryAll(".logout").length); - - await click(".logout"); - assert.ok(this.loggedOut); - }, -}); - -widgetTest("private messages - disabled", { - template: '{{mount-widget widget="user-menu"}}', - beforeEach() { - this.siteSettings.enable_personal_messages = false; - }, - - test(assert) { - assert.ok(!queryAll(".user-pms-link").length); - }, -}); - -widgetTest("private messages - enabled", { - template: '{{mount-widget widget="user-menu"}}', - beforeEach() { - this.siteSettings.enable_personal_messages = true; - }, - - async test(assert) { - const userPmsLink = queryAll(".user-pms-link")[0]; - assert.ok(userPmsLink); - await click(".user-pms-link"); - - const message = queryAll(".quick-access-panel li a")[0]; - assert.ok(message); - - assert.ok( - message.href.includes("/t/bug-can-not-render-emoji-properly/174/2"), - "should link to the next unread post" - ); - assert.ok( - message.innerHTML.includes("mixtape"), - "should include the last poster's username" - ); - assert.ok( - message.innerHTML.match(//), - "should correctly render emoji in message title" - ); - - const routeToStub = sinon.stub(DiscourseURL, "routeTo"); - await click(".user-pms-link"); - assert.ok( - routeToStub.calledWith(userPmsLink.href), - "a second click should redirect to the full private messages page" - ); - }, -}); - -widgetTest("bookmarks", { - template: '{{mount-widget widget="user-menu"}}', - - async test(assert) { - await click(".user-bookmarks-link"); - - const bookmark = queryAll(".quick-access-panel li a")[0]; - assert.ok(bookmark); - - assert.ok(bookmark.href.includes("/t/yelling-topic-title/119")); - assert.ok( - bookmark.innerHTML.includes("someguy"), - "should include the last poster's username" - ); - assert.ok( - bookmark.innerHTML.match(//), - "should correctly render emoji in bookmark title" - ); - - const routeToStub = sinon.stub(DiscourseURL, "routeTo"); - await click(".user-bookmarks-link"); - assert.ok( - routeToStub.calledWith(queryAll(".user-bookmarks-link")[0].href), - "a second click should redirect to the full bookmarks page" - ); - }, -}); - -widgetTest("anonymous", { - template: - '{{mount-widget widget="user-menu" toggleAnonymous=(action "toggleAnonymous")}}', - - beforeEach() { - this.currentUser.setProperties({ is_anonymous: false, trust_level: 3 }); - this.siteSettings.allow_anonymous_posting = true; - this.siteSettings.anonymous_posting_min_trust_level = 3; - - this.on("toggleAnonymous", () => (this.anonymous = true)); - }, - - async test(assert) { - await click(".user-preferences-link"); - assert.ok(queryAll(".enable-anonymous").length); - - await click(".enable-anonymous"); - assert.ok(this.anonymous); - }, -}); - -widgetTest("anonymous - disabled", { - template: '{{mount-widget widget="user-menu"}}', - - beforeEach() { - this.siteSettings.allow_anonymous_posting = false; - }, - - async test(assert) { - await click(".user-preferences-link"); - assert.ok(!queryAll(".enable-anonymous").length); - }, -}); - -widgetTest("anonymous - switch back", { - template: - '{{mount-widget widget="user-menu" toggleAnonymous=(action "toggleAnonymous")}}', - - beforeEach() { - this.currentUser.setProperties({ is_anonymous: true }); - this.siteSettings.allow_anonymous_posting = true; - - this.on("toggleAnonymous", () => (this.anonymous = false)); - }, - - async test(assert) { - await click(".user-preferences-link"); - assert.ok(queryAll(".disable-anonymous").length); - - await click(".disable-anonymous"); - assert.notOk(this.anonymous); - }, + assert.ok($links[4].href.includes("/g/test")); + assert.ok( + $links[4].innerHTML.includes( + I18n.t("notifications.membership_request_accepted", { + group_name: "test", + }) + ) + ); + + const routeToStub = sinon.stub(DiscourseURL, "routeTo"); + await click(".user-notifications-link"); + assert.ok( + routeToStub.calledWith(queryAll(".user-notifications-link")[0].href), + "a second click should redirect to the full notifications page" + ); + }, + }); + + componentTest("log out", { + template: '{{mount-widget widget="user-menu" logout=(action "logout")}}', + + beforeEach() { + this.on("logout", () => (this.loggedOut = true)); + }, + + async test(assert) { + await click(".user-preferences-link"); + assert.ok(queryAll(".logout").length); + + await click(".logout"); + assert.ok(this.loggedOut); + }, + }); + + componentTest("private messages - disabled", { + template: '{{mount-widget widget="user-menu"}}', + beforeEach() { + this.siteSettings.enable_personal_messages = false; + }, + + test(assert) { + assert.ok(!queryAll(".user-pms-link").length); + }, + }); + + componentTest("private messages - enabled", { + template: '{{mount-widget widget="user-menu"}}', + beforeEach() { + this.siteSettings.enable_personal_messages = true; + }, + + async test(assert) { + const userPmsLink = queryAll(".user-pms-link")[0]; + assert.ok(userPmsLink); + await click(".user-pms-link"); + + const message = queryAll(".quick-access-panel li a")[0]; + assert.ok(message); + + assert.ok( + message.href.includes("/t/bug-can-not-render-emoji-properly/174/2"), + "should link to the next unread post" + ); + assert.ok( + message.innerHTML.includes("mixtape"), + "should include the last poster's username" + ); + assert.ok( + message.innerHTML.match(//), + "should correctly render emoji in message title" + ); + + const routeToStub = sinon.stub(DiscourseURL, "routeTo"); + await click(".user-pms-link"); + assert.ok( + routeToStub.calledWith(userPmsLink.href), + "a second click should redirect to the full private messages page" + ); + }, + }); + + componentTest("bookmarks", { + template: '{{mount-widget widget="user-menu"}}', + + async test(assert) { + await click(".user-bookmarks-link"); + + const bookmark = queryAll(".quick-access-panel li a")[0]; + assert.ok(bookmark); + + assert.ok(bookmark.href.includes("/t/yelling-topic-title/119")); + assert.ok( + bookmark.innerHTML.includes("someguy"), + "should include the last poster's username" + ); + assert.ok( + bookmark.innerHTML.match(//), + "should correctly render emoji in bookmark title" + ); + + const routeToStub = sinon.stub(DiscourseURL, "routeTo"); + await click(".user-bookmarks-link"); + assert.ok( + routeToStub.calledWith(queryAll(".user-bookmarks-link")[0].href), + "a second click should redirect to the full bookmarks page" + ); + }, + }); + + componentTest("anonymous", { + template: + '{{mount-widget widget="user-menu" toggleAnonymous=(action "toggleAnonymous")}}', + + beforeEach() { + this.currentUser.setProperties({ is_anonymous: false, trust_level: 3 }); + this.siteSettings.allow_anonymous_posting = true; + this.siteSettings.anonymous_posting_min_trust_level = 3; + + this.on("toggleAnonymous", () => (this.anonymous = true)); + }, + + async test(assert) { + await click(".user-preferences-link"); + assert.ok(queryAll(".enable-anonymous").length); + + await click(".enable-anonymous"); + assert.ok(this.anonymous); + }, + }); + + componentTest("anonymous - disabled", { + template: '{{mount-widget widget="user-menu"}}', + + beforeEach() { + this.siteSettings.allow_anonymous_posting = false; + }, + + async test(assert) { + await click(".user-preferences-link"); + assert.ok(!queryAll(".enable-anonymous").length); + }, + }); + + componentTest("anonymous - switch back", { + template: + '{{mount-widget widget="user-menu" toggleAnonymous=(action "toggleAnonymous")}}', + + beforeEach() { + this.currentUser.setProperties({ is_anonymous: true }); + this.siteSettings.allow_anonymous_posting = true; + + this.on("toggleAnonymous", () => (this.anonymous = false)); + }, + + async test(assert) { + await click(".user-preferences-link"); + assert.ok(queryAll(".disable-anonymous").length); + + await click(".disable-anonymous"); + assert.notOk(this.anonymous); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js index e5da717f82..6b6350d9b7 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js @@ -1,14 +1,14 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; -import { exists } from "discourse/tests/helpers/qunit-helpers"; -import I18n from "I18n"; import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; + discourseModule, + exists, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; +import I18n from "I18n"; import { click } from "@ember/test-helpers"; -moduleForWidget("widget-dropdown"); - const DEFAULT_CONTENT = { content: [ { id: 1, label: "foo" }, @@ -60,279 +60,289 @@ const TEMPLATE = ` ) }}`; -widgetTest("dropdown id", { - template: TEMPLATE, +discourseModule("Integration | Component | Widget | widget-dropdown", function ( + hooks +) { + setupRenderingTest(hooks); - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, + componentTest("dropdown id", { + template: TEMPLATE, - test(assert) { - assert.ok(exists("#my-dropdown")); - }, -}); - -widgetTest("label", { - template: TEMPLATE, - - _translations: I18n.translations, - - beforeEach() { - I18n.translations = { en: { js: { foo: "FooBaz" } } }; - this.setProperties(DEFAULT_CONTENT); - }, - - afterEach() { - I18n.translations = this._translations; - }, - - test(assert) { - assert.equal(headerLabel(), "FooBaz"); - }, -}); - -widgetTest("translatedLabel", { - template: TEMPLATE, - - _translations: I18n.translations, - - beforeEach() { - I18n.translations = { en: { js: { foo: "FooBaz" } } }; - this.setProperties(DEFAULT_CONTENT); - this.set("translatedLabel", "BazFoo"); - }, - - afterEach() { - I18n.translations = this._translations; - }, - - test(assert) { - assert.equal(headerLabel(), this.translatedLabel); - }, -}); - -widgetTest("content", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, - - async test(assert) { - await toggle(); - assert.equal(rowById(1).dataset.id, 1, "it creates rows"); - assert.equal(rowById(2).dataset.id, 2, "it creates rows"); - assert.equal(rowById(3).dataset.id, 3, "it creates rows"); - }, -}); - -widgetTest("onChange action", { - template: ` -
- {{mount-widget - widget="widget-dropdown" - args=(hash - id="my-dropdown" - label=label - content=content - onChange=(action "onChange") - ) - }} - `, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - - this.on( - "onChange", - (item) => (this._element.querySelector("#test").innerText = item.id) - ); - }, - - async test(assert) { - await toggle(); - await clickRowById(2); - assert.equal(queryAll("#test").text(), 2, "it calls the onChange actions"); - }, -}); - -widgetTest("can be opened and closed", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, - - async test(assert) { - assert.ok(exists("#my-dropdown.closed")); - assert.ok(!exists("#my-dropdown .widget-dropdown-body")); - await toggle(); - assert.equal(rowById(2).innerText.trim(), "FooBar"); - assert.ok(exists("#my-dropdown.opened")); - assert.ok(exists("#my-dropdown .widget-dropdown-body")); - await toggle(); - assert.ok(exists("#my-dropdown.closed")); - assert.ok(!exists("#my-dropdown .widget-dropdown-body")); - }, -}); - -widgetTest("icon", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - this.set("icon", "times"); - }, - - test(assert) { - assert.ok(exists(header().querySelector(".d-icon-times"))); - }, -}); - -widgetTest("class", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - this.set("class", "activated"); - }, - - test(assert) { - assert.ok(exists("#my-dropdown.activated")); - }, -}); - -widgetTest("content with translatedLabel", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, - - async test(assert) { - await toggle(); - assert.equal(rowById(2).innerText.trim(), "FooBar"); - }, -}); - -widgetTest("content with label", { - template: TEMPLATE, - - _translations: I18n.translations, - - beforeEach() { - I18n.translations = { en: { js: { foo: "FooBaz" } } }; - this.setProperties(DEFAULT_CONTENT); - }, - - afterEach() { - I18n.translations = this._translations; - }, - - async test(assert) { - await toggle(); - assert.equal(rowById(1).innerText.trim(), "FooBaz"); - }, -}); - -widgetTest("content with icon", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, - - async test(assert) { - await toggle(); - assert.ok(exists(rowById(3).querySelector(".d-icon-times"))); - }, -}); - -widgetTest("content with html", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, - - async test(assert) { - await toggle(); - assert.equal(rowById(4).innerHTML.trim(), "baz"); - }, -}); - -widgetTest("separator", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - }, - - async test(assert) { - await toggle(); - assert.ok( - queryAll( - "#my-dropdown .widget-dropdown-item:nth-child(3)" - )[0].classList.contains("separator") - ); - }, -}); - -widgetTest("hides widget if no content", { - template: TEMPLATE, - - beforeEach() { - this.setProperties({ content: null, label: "foo" }); - }, - - test(assert) { - assert.notOk(exists("#my-dropdown .widget-dropdown-header")); - assert.notOk(exists("#my-dropdown .widget-dropdown-body")); - }, -}); - -widgetTest("headerClass option", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - this.set("options", { headerClass: "btn-small and-text" }); - }, - - test(assert) { - assert.ok(header().classList.contains("widget-dropdown-header")); - assert.ok(header().classList.contains("btn-small")); - assert.ok(header().classList.contains("and-text")); - }, -}); - -widgetTest("bodyClass option", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - this.set("options", { bodyClass: "gigantic and-yet-small" }); - }, - - async test(assert) { - await toggle(); - assert.ok(body().classList.contains("widget-dropdown-body")); - assert.ok(body().classList.contains("gigantic")); - assert.ok(body().classList.contains("and-yet-small")); - }, -}); - -widgetTest("caret option", { - template: TEMPLATE, - - beforeEach() { - this.setProperties(DEFAULT_CONTENT); - this.set("options", { caret: true }); - }, - - test(assert) { - assert.ok( - exists("#my-dropdown .widget-dropdown-header .d-icon-caret-down") - ); - }, + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + test(assert) { + assert.ok(exists("#my-dropdown")); + }, + }); + + componentTest("label", { + template: TEMPLATE, + + _translations: I18n.translations, + + beforeEach() { + I18n.translations = { en: { js: { foo: "FooBaz" } } }; + this.setProperties(DEFAULT_CONTENT); + }, + + afterEach() { + I18n.translations = this._translations; + }, + + test(assert) { + assert.equal(headerLabel(), "FooBaz"); + }, + }); + + componentTest("translatedLabel", { + template: TEMPLATE, + + _translations: I18n.translations, + + beforeEach() { + I18n.translations = { en: { js: { foo: "FooBaz" } } }; + this.setProperties(DEFAULT_CONTENT); + this.set("translatedLabel", "BazFoo"); + }, + + afterEach() { + I18n.translations = this._translations; + }, + + test(assert) { + assert.equal(headerLabel(), this.translatedLabel); + }, + }); + + componentTest("content", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + async test(assert) { + await toggle(); + assert.equal(rowById(1).dataset.id, 1, "it creates rows"); + assert.equal(rowById(2).dataset.id, 2, "it creates rows"); + assert.equal(rowById(3).dataset.id, 3, "it creates rows"); + }, + }); + + componentTest("onChange action", { + template: ` +
+ {{mount-widget + widget="widget-dropdown" + args=(hash + id="my-dropdown" + label=label + content=content + onChange=(action "onChange") + ) + }} + `, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + + this.on( + "onChange", + (item) => (this._element.querySelector("#test").innerText = item.id) + ); + }, + + async test(assert) { + await toggle(); + await clickRowById(2); + assert.equal( + queryAll("#test").text(), + 2, + "it calls the onChange actions" + ); + }, + }); + + componentTest("can be opened and closed", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + async test(assert) { + assert.ok(exists("#my-dropdown.closed")); + assert.ok(!exists("#my-dropdown .widget-dropdown-body")); + await toggle(); + assert.equal(rowById(2).innerText.trim(), "FooBar"); + assert.ok(exists("#my-dropdown.opened")); + assert.ok(exists("#my-dropdown .widget-dropdown-body")); + await toggle(); + assert.ok(exists("#my-dropdown.closed")); + assert.ok(!exists("#my-dropdown .widget-dropdown-body")); + }, + }); + + componentTest("icon", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + this.set("icon", "times"); + }, + + test(assert) { + assert.ok(exists(header().querySelector(".d-icon-times"))); + }, + }); + + componentTest("class", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + this.set("class", "activated"); + }, + + test(assert) { + assert.ok(exists("#my-dropdown.activated")); + }, + }); + + componentTest("content with translatedLabel", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + async test(assert) { + await toggle(); + assert.equal(rowById(2).innerText.trim(), "FooBar"); + }, + }); + + componentTest("content with label", { + template: TEMPLATE, + + _translations: I18n.translations, + + beforeEach() { + I18n.translations = { en: { js: { foo: "FooBaz" } } }; + this.setProperties(DEFAULT_CONTENT); + }, + + afterEach() { + I18n.translations = this._translations; + }, + + async test(assert) { + await toggle(); + assert.equal(rowById(1).innerText.trim(), "FooBaz"); + }, + }); + + componentTest("content with icon", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + async test(assert) { + await toggle(); + assert.ok(exists(rowById(3).querySelector(".d-icon-times"))); + }, + }); + + componentTest("content with html", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + async test(assert) { + await toggle(); + assert.equal(rowById(4).innerHTML.trim(), "baz"); + }, + }); + + componentTest("separator", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + }, + + async test(assert) { + await toggle(); + assert.ok( + queryAll( + "#my-dropdown .widget-dropdown-item:nth-child(3)" + )[0].classList.contains("separator") + ); + }, + }); + + componentTest("hides widget if no content", { + template: TEMPLATE, + + beforeEach() { + this.setProperties({ content: null, label: "foo" }); + }, + + test(assert) { + assert.notOk(exists("#my-dropdown .widget-dropdown-header")); + assert.notOk(exists("#my-dropdown .widget-dropdown-body")); + }, + }); + + componentTest("headerClass option", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + this.set("options", { headerClass: "btn-small and-text" }); + }, + + test(assert) { + assert.ok(header().classList.contains("widget-dropdown-header")); + assert.ok(header().classList.contains("btn-small")); + assert.ok(header().classList.contains("and-text")); + }, + }); + + componentTest("bodyClass option", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + this.set("options", { bodyClass: "gigantic and-yet-small" }); + }, + + async test(assert) { + await toggle(); + assert.ok(body().classList.contains("widget-dropdown-body")); + assert.ok(body().classList.contains("gigantic")); + assert.ok(body().classList.contains("and-yet-small")); + }, + }); + + componentTest("caret option", { + template: TEMPLATE, + + beforeEach() { + this.setProperties(DEFAULT_CONTENT); + this.set("options", { caret: true }); + }, + + test(assert) { + assert.ok( + exists("#my-dropdown .widget-dropdown-header .d-icon-caret-down") + ); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js index 93e6670986..bc6a194be8 100644 --- a/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js +++ b/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js @@ -1,416 +1,420 @@ -import { queryAll } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import componentTest, { + setupRenderingTest, +} from "discourse/tests/helpers/component-test"; import I18n from "I18n"; import { next } from "@ember/runloop"; -import { - moduleForWidget, - widgetTest, -} from "discourse/tests/helpers/widget-test"; import { createWidget } from "discourse/widgets/widget"; import { withPluginApi } from "discourse/lib/plugin-api"; import { Promise } from "rsvp"; import hbs from "discourse/widgets/hbs-compiler"; import { click } from "@ember/test-helpers"; -moduleForWidget("base"); +discourseModule("Integration | Component | Widget | base", function (hooks) { + setupRenderingTest(hooks); -widgetTest("widget attributes are passed in via args", { - template: `{{mount-widget widget="hello-test" args=args}}`, + componentTest("widget attributes are passed in via args", { + template: `{{mount-widget widget="hello-test" args=args}}`, - beforeEach() { - createWidget("hello-test", { - tagName: "div.test", - template: hbs`Hello {{attrs.name}}`, - }); + beforeEach() { + createWidget("hello-test", { + tagName: "div.test", + template: hbs`Hello {{attrs.name}}`, + }); - this.set("args", { name: "Robin" }); - }, + this.set("args", { name: "Robin" }); + }, - test(assert) { - assert.equal(queryAll(".test").text(), "Hello Robin"); - }, -}); + test(assert) { + assert.equal(queryAll(".test").text(), "Hello Robin"); + }, + }); -widgetTest("hbs template - no tagName", { - template: `{{mount-widget widget="hbs-test" args=args}}`, + componentTest("hbs template - no tagName", { + template: `{{mount-widget widget="hbs-test" args=args}}`, - beforeEach() { - createWidget("hbs-test", { - template: hbs`
Hello {{attrs.name}}
`, - }); + beforeEach() { + createWidget("hbs-test", { + template: hbs`
Hello {{attrs.name}}
`, + }); - this.set("args", { name: "Robin" }); - }, + this.set("args", { name: "Robin" }); + }, - test(assert) { - assert.equal(queryAll("div.test").text(), "Hello Robin"); - }, -}); + test(assert) { + assert.equal(queryAll("div.test").text(), "Hello Robin"); + }, + }); -widgetTest("hbs template - with tagName", { - template: `{{mount-widget widget="hbs-test" args=args}}`, + componentTest("hbs template - with tagName", { + template: `{{mount-widget widget="hbs-test" args=args}}`, - beforeEach() { - createWidget("hbs-test", { - tagName: "div.test", - template: hbs`Hello {{attrs.name}}`, - }); + beforeEach() { + createWidget("hbs-test", { + tagName: "div.test", + template: hbs`Hello {{attrs.name}}`, + }); - this.set("args", { name: "Robin" }); - }, + this.set("args", { name: "Robin" }); + }, - test(assert) { - assert.equal(queryAll("div.test").text(), "Hello Robin"); - }, -}); + test(assert) { + assert.equal(queryAll("div.test").text(), "Hello Robin"); + }, + }); -widgetTest("hbs template - with data attributes", { - template: `{{mount-widget widget="hbs-test" args=args}}`, + componentTest("hbs template - with data attributes", { + template: `{{mount-widget widget="hbs-test" args=args}}`, - beforeEach() { - createWidget("hbs-test", { - template: hbs`
`, - }); - }, + beforeEach() { + createWidget("hbs-test", { + template: hbs`
`, + }); + }, - test(assert) { - assert.equal(queryAll("div.mydiv").data("my-test"), "hello world"); - }, -}); + test(assert) { + assert.equal(queryAll("div.mydiv").data("my-test"), "hello world"); + }, + }); -widgetTest("buildClasses", { - template: `{{mount-widget widget="classname-test" args=args}}`, + componentTest("buildClasses", { + template: `{{mount-widget widget="classname-test" args=args}}`, - beforeEach() { - createWidget("classname-test", { - tagName: "div.test", + beforeEach() { + createWidget("classname-test", { + tagName: "div.test", - buildClasses(attrs) { - return ["static", attrs.dynamic]; - }, - }); - - this.set("args", { dynamic: "cool-class" }); - }, - - test(assert) { - assert.ok( - queryAll(".test.static.cool-class").length, - "it has all the classes" - ); - }, -}); - -widgetTest("buildAttributes", { - template: `{{mount-widget widget="attributes-test" args=args}}`, - - beforeEach() { - createWidget("attributes-test", { - tagName: "div.test", - - buildAttributes(attrs) { - return { "data-evil": "trout", "aria-label": attrs.label }; - }, - }); - - this.set("args", { label: "accessibility" }); - }, - - test(assert) { - assert.ok(queryAll(".test[data-evil=trout]").length); - assert.ok(queryAll(".test[aria-label=accessibility]").length); - }, -}); - -widgetTest("buildId", { - template: `{{mount-widget widget="id-test" args=args}}`, - - beforeEach() { - createWidget("id-test", { - buildId(attrs) { - return `test-${attrs.id}`; - }, - }); - - this.set("args", { id: 1234 }); - }, - - test(assert) { - assert.ok(queryAll("#test-1234").length); - }, -}); - -widgetTest("widget state", { - template: `{{mount-widget widget="state-test"}}`, - - beforeEach() { - createWidget("state-test", { - tagName: "button.test", - buildKey: () => `button-test`, - template: hbs`{{state.clicks}} clicks`, - - defaultState() { - return { clicks: 0 }; - }, - - click() { - this.state.clicks++; - }, - }); - }, - - async test(assert) { - assert.ok(queryAll("button.test").length, "it renders the button"); - assert.equal(queryAll("button.test").text(), "0 clicks"); - - await click(queryAll("button")); - assert.equal(queryAll("button.test").text(), "1 clicks"); - }, -}); - -widgetTest("widget update with promise", { - template: `{{mount-widget widget="promise-test"}}`, - - beforeEach() { - createWidget("promise-test", { - tagName: "button.test", - buildKey: () => "promise-test", - template: hbs` - {{#if state.name}} - {{state.name}} - {{else}} - No name - {{/if}} - `, - - click() { - return new Promise((resolve) => { - next(() => { - this.state.name = "Robin"; - resolve(); - }); - }); - }, - }); - }, - - async test(assert) { - assert.equal(queryAll("button.test").text().trim(), "No name"); - - await click(queryAll("button")); - assert.equal(queryAll("button.test").text().trim(), "Robin"); - }, -}); - -widgetTest("widget attaching", { - template: `{{mount-widget widget="attach-test"}}`, - - beforeEach() { - createWidget("test-embedded", { tagName: "div.embedded" }); - - createWidget("attach-test", { - tagName: "div.container", - template: hbs`{{attach widget="test-embedded" attrs=attrs}}`, - }); - }, - - test(assert) { - assert.ok(queryAll(".container").length, "renders container"); - assert.ok(queryAll(".container .embedded").length, "renders attached"); - }, -}); - -widgetTest("magic attaching by name", { - template: `{{mount-widget widget="attach-test"}}`, - - beforeEach() { - createWidget("test-embedded", { tagName: "div.embedded" }); - - createWidget("attach-test", { - tagName: "div.container", - template: hbs`{{test-embedded attrs=attrs}}`, - }); - }, - - test(assert) { - assert.ok(queryAll(".container").length, "renders container"); - assert.ok(queryAll(".container .embedded").length, "renders attached"); - }, -}); - -widgetTest("custom attrs to a magic attached widget", { - template: `{{mount-widget widget="attach-test"}}`, - - beforeEach() { - createWidget("testing", { - tagName: "span.value", - template: hbs`{{attrs.value}}`, - }); - - createWidget("attach-test", { - tagName: "div.container", - template: hbs`{{testing value=(concat "hello" " " "world")}}`, - }); - }, - - test(assert) { - assert.ok(queryAll(".container").length, "renders container"); - assert.equal(queryAll(".container .value").text(), "hello world"); - }, -}); - -widgetTest("handlebars d-icon", { - template: `{{mount-widget widget="hbs-icon-test" args=args}}`, - - beforeEach() { - createWidget("hbs-icon-test", { - template: hbs`{{d-icon "arrow-down"}}`, - }); - }, - - test(assert) { - assert.equal(queryAll(".d-icon-arrow-down").length, 1); - }, -}); - -widgetTest("handlebars i18n", { - _translations: I18n.translations, - - template: `{{mount-widget widget="hbs-i18n-test" args=args}}`, - - beforeEach() { - createWidget("hbs-i18n-test", { - template: hbs` - {{i18n "hbs_test0"}} - {{i18n attrs.key}} - test - `, - }); - I18n.translations = { - en: { - js: { - hbs_test0: "evil", - hbs_test1: "trout", + buildClasses(attrs) { + return ["static", attrs.dynamic]; }, - }, - }; - this.set("args", { key: "hbs_test1" }); - }, - - afterEach() { - I18n.translations = this._translations; - }, - - test(assert) { - // comin up - assert.equal(queryAll("span.string").text(), "evil"); - assert.equal(queryAll("span.var").text(), "trout"); - assert.equal(queryAll("a").prop("title"), "evil"); - }, -}); - -widgetTest("handlebars #each", { - template: `{{mount-widget widget="hbs-each-test" args=args}}`, - - beforeEach() { - createWidget("hbs-each-test", { - tagName: "ul", - template: hbs` - {{#each attrs.items as |item|}} -
  • {{item}}
  • - {{/each}} - `, - }); - - this.set("args", { - items: ["one", "two", "three"], - }); - }, - - test(assert) { - assert.equal(queryAll("ul li").length, 3); - assert.equal(queryAll("ul li:eq(0)").text(), "one"); - }, -}); - -widgetTest("widget decorating", { - template: `{{mount-widget widget="decorate-test"}}`, - - beforeEach() { - createWidget("decorate-test", { - tagName: "div.decorate", - template: hbs`main content`, - }); - - withPluginApi("0.1", (api) => { - api.decorateWidget("decorate-test:before", (dec) => { - return dec.h("b", "before"); }); - api.decorateWidget("decorate-test:after", (dec) => { - return dec.h("i", "after"); + this.set("args", { dynamic: "cool-class" }); + }, + + test(assert) { + assert.ok( + queryAll(".test.static.cool-class").length, + "it has all the classes" + ); + }, + }); + + componentTest("buildAttributes", { + template: `{{mount-widget widget="attributes-test" args=args}}`, + + beforeEach() { + createWidget("attributes-test", { + tagName: "div.test", + + buildAttributes(attrs) { + return { "data-evil": "trout", "aria-label": attrs.label }; + }, }); - }); - }, - test(assert) { - assert.ok(queryAll(".decorate").length); - assert.equal(queryAll(".decorate b").text(), "before"); - assert.equal(queryAll(".decorate i").text(), "after"); - }, -}); - -widgetTest("widget settings", { - template: `{{mount-widget widget="settings-test"}}`, - - beforeEach() { - createWidget("settings-test", { - tagName: "div.settings", - template: hbs`age is {{settings.age}}`, - settings: { age: 36 }, - }); - }, - - test(assert) { - assert.equal(queryAll(".settings").text(), "age is 36"); - }, -}); - -widgetTest("override settings", { - template: `{{mount-widget widget="ov-settings-test"}}`, - - beforeEach() { - createWidget("ov-settings-test", { - tagName: "div.settings", - template: hbs`age is {{settings.age}}`, - settings: { age: 36 }, - }); - - withPluginApi("0.1", (api) => { - api.changeWidgetSetting("ov-settings-test", "age", 37); - }); - }, - - test(assert) { - assert.equal(queryAll(".settings").text(), "age is 37"); - }, -}); - -widgetTest("get accessor", { - template: `{{mount-widget widget="get-accessor-test"}}`, - - beforeEach() { - createWidget("get-accessor-test", { - tagName: "div.test", - template: hbs`Hello {{transformed.name}}`, - transform() { - return { - name: this.get("currentUser.username"), - }; - }, - }); - }, - - test(assert) { - assert.equal(queryAll("div.test").text(), "Hello eviltrout"); - }, + this.set("args", { label: "accessibility" }); + }, + + test(assert) { + assert.ok(queryAll(".test[data-evil=trout]").length); + assert.ok(queryAll(".test[aria-label=accessibility]").length); + }, + }); + + componentTest("buildId", { + template: `{{mount-widget widget="id-test" args=args}}`, + + beforeEach() { + createWidget("id-test", { + buildId(attrs) { + return `test-${attrs.id}`; + }, + }); + + this.set("args", { id: 1234 }); + }, + + test(assert) { + assert.ok(queryAll("#test-1234").length); + }, + }); + + componentTest("widget state", { + template: `{{mount-widget widget="state-test"}}`, + + beforeEach() { + createWidget("state-test", { + tagName: "button.test", + buildKey: () => `button-test`, + template: hbs`{{state.clicks}} clicks`, + + defaultState() { + return { clicks: 0 }; + }, + + click() { + this.state.clicks++; + }, + }); + }, + + async test(assert) { + assert.ok(queryAll("button.test").length, "it renders the button"); + assert.equal(queryAll("button.test").text(), "0 clicks"); + + await click(queryAll("button")); + assert.equal(queryAll("button.test").text(), "1 clicks"); + }, + }); + + componentTest("widget update with promise", { + template: `{{mount-widget widget="promise-test"}}`, + + beforeEach() { + createWidget("promise-test", { + tagName: "button.test", + buildKey: () => "promise-test", + template: hbs` + {{#if state.name}} + {{state.name}} + {{else}} + No name + {{/if}} + `, + + click() { + return new Promise((resolve) => { + next(() => { + this.state.name = "Robin"; + resolve(); + }); + }); + }, + }); + }, + + async test(assert) { + assert.equal(queryAll("button.test").text().trim(), "No name"); + + await click(queryAll("button")); + assert.equal(queryAll("button.test").text().trim(), "Robin"); + }, + }); + + componentTest("widget attaching", { + template: `{{mount-widget widget="attach-test"}}`, + + beforeEach() { + createWidget("test-embedded", { tagName: "div.embedded" }); + + createWidget("attach-test", { + tagName: "div.container", + template: hbs`{{attach widget="test-embedded" attrs=attrs}}`, + }); + }, + + test(assert) { + assert.ok(queryAll(".container").length, "renders container"); + assert.ok(queryAll(".container .embedded").length, "renders attached"); + }, + }); + + componentTest("magic attaching by name", { + template: `{{mount-widget widget="attach-test"}}`, + + beforeEach() { + createWidget("test-embedded", { tagName: "div.embedded" }); + + createWidget("attach-test", { + tagName: "div.container", + template: hbs`{{test-embedded attrs=attrs}}`, + }); + }, + + test(assert) { + assert.ok(queryAll(".container").length, "renders container"); + assert.ok(queryAll(".container .embedded").length, "renders attached"); + }, + }); + + componentTest("custom attrs to a magic attached widget", { + template: `{{mount-widget widget="attach-test"}}`, + + beforeEach() { + createWidget("testing", { + tagName: "span.value", + template: hbs`{{attrs.value}}`, + }); + + createWidget("attach-test", { + tagName: "div.container", + template: hbs`{{testing value=(concat "hello" " " "world")}}`, + }); + }, + + test(assert) { + assert.ok(queryAll(".container").length, "renders container"); + assert.equal(queryAll(".container .value").text(), "hello world"); + }, + }); + + componentTest("handlebars d-icon", { + template: `{{mount-widget widget="hbs-icon-test" args=args}}`, + + beforeEach() { + createWidget("hbs-icon-test", { + template: hbs`{{d-icon "arrow-down"}}`, + }); + }, + + test(assert) { + assert.equal(queryAll(".d-icon-arrow-down").length, 1); + }, + }); + + componentTest("handlebars i18n", { + _translations: I18n.translations, + + template: `{{mount-widget widget="hbs-i18n-test" args=args}}`, + + beforeEach() { + createWidget("hbs-i18n-test", { + template: hbs` + {{i18n "hbs_test0"}} + {{i18n attrs.key}} + test + `, + }); + I18n.translations = { + en: { + js: { + hbs_test0: "evil", + hbs_test1: "trout", + }, + }, + }; + this.set("args", { key: "hbs_test1" }); + }, + + afterEach() { + I18n.translations = this._translations; + }, + + test(assert) { + // comin up + assert.equal(queryAll("span.string").text(), "evil"); + assert.equal(queryAll("span.var").text(), "trout"); + assert.equal(queryAll("a").prop("title"), "evil"); + }, + }); + + componentTest("handlebars #each", { + template: `{{mount-widget widget="hbs-each-test" args=args}}`, + + beforeEach() { + createWidget("hbs-each-test", { + tagName: "ul", + template: hbs` + {{#each attrs.items as |item|}} +
  • {{item}}
  • + {{/each}} + `, + }); + + this.set("args", { + items: ["one", "two", "three"], + }); + }, + + test(assert) { + assert.equal(queryAll("ul li").length, 3); + assert.equal(queryAll("ul li:eq(0)").text(), "one"); + }, + }); + + componentTest("widget decorating", { + template: `{{mount-widget widget="decorate-test"}}`, + + beforeEach() { + createWidget("decorate-test", { + tagName: "div.decorate", + template: hbs`main content`, + }); + + withPluginApi("0.1", (api) => { + api.decorateWidget("decorate-test:before", (dec) => { + return dec.h("b", "before"); + }); + + api.decorateWidget("decorate-test:after", (dec) => { + return dec.h("i", "after"); + }); + }); + }, + + test(assert) { + assert.ok(queryAll(".decorate").length); + assert.equal(queryAll(".decorate b").text(), "before"); + assert.equal(queryAll(".decorate i").text(), "after"); + }, + }); + + componentTest("widget settings", { + template: `{{mount-widget widget="settings-test"}}`, + + beforeEach() { + createWidget("settings-test", { + tagName: "div.settings", + template: hbs`age is {{settings.age}}`, + settings: { age: 36 }, + }); + }, + + test(assert) { + assert.equal(queryAll(".settings").text(), "age is 36"); + }, + }); + + componentTest("override settings", { + template: `{{mount-widget widget="ov-settings-test"}}`, + + beforeEach() { + createWidget("ov-settings-test", { + tagName: "div.settings", + template: hbs`age is {{settings.age}}`, + settings: { age: 36 }, + }); + + withPluginApi("0.1", (api) => { + api.changeWidgetSetting("ov-settings-test", "age", 37); + }); + }, + + test(assert) { + assert.equal(queryAll(".settings").text(), "age is 37"); + }, + }); + + componentTest("get accessor", { + template: `{{mount-widget widget="get-accessor-test"}}`, + + beforeEach() { + createWidget("get-accessor-test", { + tagName: "div.test", + template: hbs`Hello {{transformed.name}}`, + transform() { + return { + name: this.get("currentUser.username"), + }; + }, + }); + }, + + test(assert) { + assert.equal(queryAll("div.test").text(), "Hello eviltrout"); + }, + }); }); diff --git a/app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js b/app/assets/javascripts/discourse/tests/unit/components/group-membership-button-test.js similarity index 97% rename from app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js rename to app/assets/javascripts/discourse/tests/unit/components/group-membership-button-test.js index 331c3daffc..58e2eb3855 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js +++ b/app/assets/javascripts/discourse/tests/unit/components/group-membership-button-test.js @@ -1,6 +1,7 @@ import { test } from "qunit"; import { moduleFor } from "ember-qunit"; +// TODO: Convert to a modern *integration* test moduleFor("component:group-membership-button"); test("canJoinGroup", function (assert) { diff --git a/app/assets/javascripts/discourse/tests/unit/lib/keyboard-shortcuts-test.js b/app/assets/javascripts/discourse/tests/unit/lib/keyboard-shortcuts-test.js new file mode 100644 index 0000000000..56f77dcf35 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/unit/lib/keyboard-shortcuts-test.js @@ -0,0 +1,156 @@ +import { test, module } from "qunit"; +import DiscourseURL from "discourse/lib/url"; +import sinon from "sinon"; +import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts"; + +let testMouseTrap; + +module("Unit | Utility | keyboard-shortcuts", function (hooks) { + hooks.beforeEach(function () { + let _bindings = {}; + + testMouseTrap = { + bind: function (bindings, callback) { + let registerBinding = function (binding) { + _bindings[binding] = callback; + }.bind(this); + + if (Array.isArray(bindings)) { + bindings.forEach(registerBinding, this); + } else { + registerBinding(bindings); + } + }, + + trigger: function (binding) { + _bindings[binding].call(); + }, + }; + + sinon.stub(DiscourseURL, "routeTo"); + + $("#qunit-fixture").html( + [ + "
    ", + "" + "
    ", + "
    ", + "
      ", + "
    • ", + "
    • ", + "
    • ", + "
    • ", + "
    ", + "
    ", + "", + " ", + "
    ", + " ", + "
    ", + "", + "
    ", + "", + "
    ", + "
    ", + "
    ", + "
    ", + "
    ", + ].join("\n") + ); + }); + + hooks.afterEach(function () { + $("#qunit-scratch").html(""); + testMouseTrap = undefined; + }); + + let pathBindings = KeyboardShortcuts.PATH_BINDINGS || {}; + Object.keys(pathBindings).forEach((path) => { + const binding = pathBindings[path]; + let testName = binding + " goes to " + path; + + test(testName, function (assert) { + KeyboardShortcuts.bindEvents(); + testMouseTrap.trigger(binding); + + assert.ok(DiscourseURL.routeTo.calledWith(path)); + }); + }); + + let clickBindings = KeyboardShortcuts.CLICK_BINDINGS || {}; + Object.keys(clickBindings).forEach((selector) => { + const binding = clickBindings[selector]; + let bindings = binding.split(","); + + let testName = binding + " clicks on " + selector; + + test(testName, function (assert) { + KeyboardShortcuts.bindEvents(); + $(selector).on("click", function () { + assert.ok(true, selector + " was clicked"); + }); + + bindings.forEach(function (b) { + testMouseTrap.trigger(b); + }, this); + }); + }); + + let functionBindings = KeyboardShortcuts.FUNCTION_BINDINGS || {}; + Object.keys(functionBindings).forEach((func) => { + const binding = functionBindings[func]; + let testName = binding + " calls " + func; + + test(testName, function (assert) { + sinon.stub(KeyboardShortcuts, func, function () { + assert.ok(true, func + " is called when " + binding + " is triggered"); + }); + KeyboardShortcuts.bindEvents(); + + testMouseTrap.trigger(binding); + }); + }); + + test("selectDown calls _moveSelection with 1", function (assert) { + let stub = sinon.stub(KeyboardShortcuts, "_moveSelection"); + + KeyboardShortcuts.selectDown(); + assert.ok(stub.calledWith(1), "_moveSelection is called with 1"); + }); + + test("selectUp calls _moveSelection with -1", function (assert) { + let stub = sinon.stub(KeyboardShortcuts, "_moveSelection"); + + KeyboardShortcuts.selectUp(); + assert.ok(stub.calledWith(-1), "_moveSelection is called with -1"); + }); + + test("goBack calls history.back", function (assert) { + let called = false; + sinon.stub(history, "back").callsFake(function () { + called = true; + }); + + KeyboardShortcuts.goBack(); + assert.ok(called, "history.back is called"); + }); + + test("nextSection calls _changeSection with 1", function (assert) { + let spy = sinon.spy(KeyboardShortcuts, "_changeSection"); + + KeyboardShortcuts.nextSection(); + assert.ok(spy.calledWith(1), "_changeSection is called with 1"); + }); + + test("prevSection calls _changeSection with -1", function (assert) { + let spy = sinon.spy(KeyboardShortcuts, "_changeSection"); + + KeyboardShortcuts.prevSection(); + assert.ok(spy.calledWith(-1), "_changeSection is called with -1"); + }); +}); diff --git a/app/assets/javascripts/test-shims.js b/app/assets/javascripts/test-shims.js index 31b717adca..da575b1e6d 100644 --- a/app/assets/javascripts/test-shims.js +++ b/app/assets/javascripts/test-shims.js @@ -29,6 +29,7 @@ define("@ember/test-helpers", () => { async settled() { // No-op in pre ember-cli environment }, + TestModuleForComponent: window.TestModuleForComponent, }; [ "click", diff --git a/vendor/assets/javascripts/ember-qunit.js b/vendor/assets/javascripts/ember-qunit.js index 56c152db4a..f8e2d0ce97 100644 --- a/vendor/assets/javascripts/ember-qunit.js +++ b/vendor/assets/javascripts/ember-qunit.js @@ -1987,4 +1987,7 @@ for (var exportName in emberQUnit) { window[exportName] = emberQUnit[exportName]; } +var emberTestHelpers = requireModule("ember-test-helpers"); +window.TestModuleForComponent = emberTestHelpers.TestModuleForComponent; + })(); From 7539c2ed7fe7ae991c823bc958be12fa052eb830 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Fri, 20 Nov 2020 10:44:34 -0500 Subject: [PATCH 08/71] UX: Revamp category security tab (#11273) --- .../app/components/category-permission-row.js | 123 ++++++++++++++++ .../app/components/edit-category-security.js | 87 +++-------- .../app/components/edit-category-tab.js | 3 +- .../app/controllers/edit-category-tabs.js | 6 + .../discourse/app/models/category.js | 32 +++- .../components/category-permission-row.hbs | 29 ++++ .../components/edit-category-general.hbs | 6 +- .../components/edit-category-security.hbs | 90 +++++------- .../components/edit-category-settings.hbs | 2 - .../edit-category-topic-template.hbs | 1 - .../app/templates/edit-category-tabs.hbs | 10 +- .../acceptance/category-edit-security-test.js | 139 +++++++++++++----- .../common/base/edit-category.scss | 90 ++++++++++-- .../stylesheets/mobile/edit-category.scss | 24 +-- config/locales/client.en.yml | 11 ++ 15 files changed, 454 insertions(+), 199 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/category-permission-row.js create mode 100644 app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs diff --git a/app/assets/javascripts/discourse/app/components/category-permission-row.js b/app/assets/javascripts/discourse/app/components/category-permission-row.js new file mode 100644 index 0000000000..881f33a9ba --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/category-permission-row.js @@ -0,0 +1,123 @@ +import I18n from "I18n"; +import Component from "@ember/component"; +import discourseComputed, { observes } from "discourse-common/utils/decorators"; +import PermissionType from "discourse/models/permission-type"; +import { equal, alias } from "@ember/object/computed"; + +const EVERYONE = "everyone"; + +export default Component.extend({ + classNames: ["permission-row", "row-body"], + canCreate: equal("type", PermissionType.FULL), + everyonePermissionType: alias("everyonePermission.permission_type"), + + @discourseComputed("type") + canReply(value) { + return ( + value === PermissionType.CREATE_POST || value === PermissionType.FULL + ); + }, + + @discourseComputed("type") + canReplyIcon() { + return this.canReply ? "check-square" : "far-square"; + }, + + @discourseComputed("type") + canCreateIcon() { + return this.canCreate ? "check-square" : "far-square"; + }, + + @discourseComputed("type") + replyGranted() { + return this.type <= PermissionType.CREATE_POST ? "reply-granted" : ""; + }, + + @discourseComputed("type") + createGranted() { + return this.type === PermissionType.FULL ? "create-granted" : ""; + }, + + @observes("everyonePermissionType") + inheritFromEveryone() { + if (this.group_name === EVERYONE) { + return; + } + + // groups cannot have a lesser permission than "everyone" + if (this.everyonePermissionType < this.type) { + this.updatePermission(this.everyonePermissionType); + } + }, + + @discourseComputed("everyonePermissionType", "type") + replyDisabled(everyonePermissionType) { + if ( + this.group_name !== EVERYONE && + everyonePermissionType && + everyonePermissionType <= PermissionType.CREATE_POST + ) { + return true; + } + return false; + }, + + @discourseComputed("replyDisabled") + replyTooltip() { + return this.replyDisabled + ? I18n.t("category.permissions.inherited") + : I18n.t("category.permissions.toggle_reply"); + }, + + @discourseComputed("everyonePermissionType", "type") + createDisabled(everyonePermissionType) { + if ( + this.group_name !== EVERYONE && + everyonePermissionType && + everyonePermissionType === PermissionType.FULL + ) { + return true; + } + return false; + }, + + @discourseComputed("createDisabled") + createTooltip() { + return this.createDisabled + ? I18n.t("category.permissions.inherited") + : I18n.t("category.permissions.toggle_full"); + }, + + updatePermission(type) { + this.category.updatePermission(this.group_name, type); + }, + + actions: { + removeRow() { + this.category.removePermission(this.group_name); + }, + + setPermissionReply() { + if (this.type <= PermissionType.CREATE_POST) { + this.updatePermission(PermissionType.READONLY); + } else { + this.updatePermission(PermissionType.CREATE_POST); + } + }, + + setPermissionFull() { + if ( + this.group_name !== EVERYONE && + this.everyonePermissionType === PermissionType.FULL + ) { + return; + } + + if (this.type === PermissionType.FULL) { + this.updatePermission(PermissionType.CREATE_POST); + } else { + this.updatePermission(PermissionType.FULL); + } + }, + }, +}); diff --git a/app/assets/javascripts/discourse/app/components/edit-category-security.js b/app/assets/javascripts/discourse/app/components/edit-category-security.js index a85558ff8a..cd69ba5043 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-security.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-security.js @@ -1,78 +1,39 @@ import { buildCategoryPanel } from "discourse/components/edit-category-panel"; import PermissionType from "discourse/models/permission-type"; -import { on } from "discourse-common/utils/decorators"; +import discourseComputed from "discourse-common/utils/decorators"; + +import { not } from "@ember/object/computed"; export default buildCategoryPanel("security", { - editingPermissions: false, selectedGroup: null, - selectedPermission: null, - showPendingGroupChangesAlert: false, - interactedWithDropdowns: false, + noGroupSelected: not("selectedGroup"), - @on("init") - _setup() { - this.setProperties({ - selectedGroup: this.get("category.availableGroups.firstObject"), - selectedPermission: this.get( - "category.availablePermissions.firstObject.id" - ), - }); + @discourseComputed("category.permissions.@each.permission_type") + everyonePermission(permissions) { + return permissions.findBy("group_name", "everyone"); }, - @on("init") - _registerValidator() { - this.registerValidator(() => { - if ( - !this.showPendingGroupChangesAlert && - this.interactedWithDropdowns && - this.activeTab - ) { - this.set("showPendingGroupChangesAlert", true); - return true; - } - }); + @discourseComputed("category.permissions.@each.permission_type") + everyoneGrantedFull() { + return ( + this.everyonePermission && + this.everyonePermission.permission_type === PermissionType.FULL + ); + }, + + @discourseComputed("everyonePermission") + minimumPermission(everyonePermission) { + return everyonePermission + ? everyonePermission.permission_type + : PermissionType.READONLY; }, actions: { - onSelectGroup(selectedGroup) { - this.setProperties({ - interactedWithDropdowns: true, - selectedGroup, + onSelectGroup(group_name) { + this.category.addPermission({ + group_name, + permission_type: this.minimumPermission, }); }, - - onSelectPermission(selectedPermission) { - this.setProperties({ - interactedWithDropdowns: true, - selectedPermission, - }); - }, - - editPermissions() { - if (!this.get("category.is_special")) { - this.set("editingPermissions", true); - } - }, - - addPermission(group, id) { - if (!this.get("category.is_special")) { - this.category.addPermission({ - group_name: group + "", - permission: PermissionType.create({ id: parseInt(id, 10) }), - }); - } - - this.setProperties({ - selectedGroup: this.get("category.availableGroups.firstObject"), - showPendingGroupChangesAlert: false, - interactedWithDropdowns: false, - }); - }, - - removePermission(permission) { - if (!this.get("category.is_special")) { - this.category.removePermission(permission); - } - }, }, }); diff --git a/app/assets/javascripts/discourse/app/components/edit-category-tab.js b/app/assets/javascripts/discourse/app/components/edit-category-tab.js index eb0f52c36a..0ead526caa 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-tab.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-tab.js @@ -6,6 +6,7 @@ import { propertyEqual } from "discourse/lib/computed"; import getURL from "discourse-common/lib/get-url"; import { empty } from "@ember/object/computed"; import DiscourseURL from "discourse/lib/url"; +import { underscore } from "@ember/string"; export default Component.extend({ tagName: "li", @@ -21,7 +22,7 @@ export default Component.extend({ @discourseComputed("tab") title(tab) { - return I18n.t("category." + tab.replace("-", "_")); + return I18n.t(`category.${underscore(tab)}`); }, didInsertElement() { diff --git a/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js b/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js index 2f38469f8e..cff351a23a 100644 --- a/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js +++ b/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js @@ -7,6 +7,7 @@ import DiscourseURL from "discourse/lib/url"; import { readOnly } from "@ember/object/computed"; import PermissionType from "discourse/models/permission-type"; import { NotificationLevels } from "discourse/lib/notification-levels"; +import { underscore } from "@ember/string"; export default Controller.extend({ selectedTab: "general", @@ -69,6 +70,11 @@ export default Controller.extend({ : I18n.t("category.create"); }, + @discourseComputed("selectedTab") + selectedTabTitle(tab) { + return I18n.t(`category.${underscore(tab)}`); + }, + actions: { registerValidator(validator) { this.validators.push(validator); diff --git a/app/assets/javascripts/discourse/app/models/category.js b/app/assets/javascripts/discourse/app/models/category.js index f504c9f368..b1dbd782d2 100644 --- a/app/assets/javascripts/discourse/app/models/category.js +++ b/app/assets/javascripts/discourse/app/models/category.js @@ -10,6 +10,8 @@ import Site from "discourse/models/site"; import User from "discourse/models/user"; import { getOwner } from "discourse-common/lib/get-owner"; +const STAFF_GROUP_NAME = "staff"; + const Category = RestModel.extend({ permissions: null, @@ -22,15 +24,13 @@ const Category = RestModel.extend({ this.set("availableGroups", availableGroups); const groupPermissions = this.group_permissions; + if (groupPermissions) { this.set( "permissions", groupPermissions.map((elem) => { availableGroups.removeObject(elem.group_name); - return { - group_name: elem.group_name, - permission: PermissionType.create({ id: elem.permission_type }), - }; + return elem; }) ); } @@ -231,7 +231,12 @@ const Category = RestModel.extend({ _permissionsForUpdate() { const permissions = this.permissions; let rval = {}; - permissions.forEach((p) => (rval[p.group_name] = p.permission.id)); + if (permissions.length) { + permissions.forEach((p) => (rval[p.group_name] = p.permission_type)); + } else { + // empty permissions => staff-only access + rval[STAFF_GROUP_NAME] = PermissionType.FULL; + } return rval; }, @@ -246,9 +251,20 @@ const Category = RestModel.extend({ this.availableGroups.removeObject(permission.group_name); }, - removePermission(permission) { - this.permissions.removeObject(permission); - this.availableGroups.addObject(permission.group_name); + removePermission(group_name) { + const permission = this.permissions.findBy("group_name", group_name); + if (permission) { + this.permissions.removeObject(permission); + this.availableGroups.addObject(group_name); + } + }, + + updatePermission(group_name, type) { + this.permissions.forEach((p, i) => { + if (p.group_name === group_name) { + this.set(`permissions.${i}.permission_type`, type); + } + }); }, @discourseComputed("topics") diff --git a/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs b/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs new file mode 100644 index 0000000000..b3ac950558 --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs @@ -0,0 +1,29 @@ + + {{group_name}} + + {{d-icon "far-trash-alt"}} + + + + {{d-button + icon="check-square" + class="btn btn-flat see" + disabled=true + }} + + {{d-button + icon=canReplyIcon + action=(action "setPermissionReply") + translatedTitle=replyTooltip + class=(concat "btn btn-flat reply-toggle " replyGranted) + disabled=replyDisabled + }} + + {{d-button + icon=canCreateIcon + action=(action "setPermissionFull") + translatedTitle=createTooltip + class=(concat "btn btn-flat create-toggle " createGranted) + disabled=createDisabled + }} + diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs index 23e2981f00..e5ec4c44d1 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs @@ -12,14 +12,16 @@
    {{category-chooser - rootNone=true value=category.parent_category_id - excludeCategoryId=category.id categories=parentCategories allowSubCategories=true allowUncategorized=false allowRestrictedCategories=true onChange=(action (mut category.parent_category_id)) + options=(hash + excludeCategoryId=category.id + none=true + ) }}
    {{/if}} diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs index 91978bec1c..41714758a1 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs @@ -6,62 +6,50 @@

    {{i18n "category.special_warning"}}

    {{/if}} {{/if}} - {{#unless category.isUncategorizedCategory}} -
      + + {{#unless category.is_special}} +
      +
      + {{i18n "category.permissions.group"}} + + {{i18n "category.permissions.see"}} + {{i18n "category.permissions.reply"}} + {{i18n "category.permissions.create"}} + +
      {{#each category.permissions as |p|}} -
    • - {{p.group_name}} - {{html-safe (i18n "category.can")}} - {{p.permission.description}} - {{#if editingPermissions}} - {{d-icon "times-circle"}} - {{/if}} -
    • + {{category-permission-row group_name=p.group_name type=p.permission_type category=category everyonePermission=everyonePermission}} {{/each}} -
    - {{/unless}} - {{#if editingPermissions}} - {{#if category.availableGroups}} - {{combo-box - class="available-groups" - content=category.availableGroups - onChange=(action "onSelectGroup") - value=selectedGroup - valueProperty=null - nameProperty=null - options=(hash - placementStrategy="absolute" - ) - }} - {{combo-box - class="permission-selector" - nameProperty="description" - content=category.availablePermissions - onChange=(action "onSelectPermission") - value=selectedPermission - options=(hash - placementStrategy="absolute" - ) - }} - {{d-button - action=(action "addPermission" selectedGroup selectedPermission) - class="btn-primary add-permission" - icon="plus"}} - {{#if showPendingGroupChangesAlert}} -
    -
    - {{i18n "category.pending_permission_change_alert" group=selectedGroup}} + + {{#unless category.permissions}} +
    + {{i18n "category.permissions.no_groups_selected"}} +
    + {{/unless}} + + {{#if category.availableGroups}} +
    + + {{combo-box + class="available-groups" + content=category.availableGroups + onChange=(action "onSelectGroup") + value=null + valueProperty=null + nameProperty=null + options=(hash + none="category.security_add_group" + ) + }} +
    {{/if}} +
    + + {{#if everyoneGrantedFull}} +

    {{i18n "category.permissions.everyone_has_access"}}

    {{/if}} - {{else}} - {{#unless category.is_special}} - {{d-button - action=(action "editPermissions") - class="btn-default edit-permission" - label="category.edit_permissions"}} - {{/unless}} - {{/if}} + {{/unless}} {{plugin-outlet name="category-custom-security" args=(hash category=category) connectorTagName="" tagName="section"}} diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs index f847963bfc..33e59c3a3b 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs @@ -1,6 +1,4 @@
    -

    {{i18n "category.settings_sections.general"}}

    - {{#if showPositionInput}}