From 21e8a33177047e69f25c7103480c06ba8e23d01c Mon Sep 17 00:00:00 2001
From: Jarek Radosz <jradosz@gmail.com>
Date: Tue, 8 Jun 2021 17:54:12 +0200
Subject: [PATCH] DEV: Clean up QUnit tests (#13328)

* DEV: Use `query` helper instead of `queryAll()[0]`
* DEV: Replace `queryAll().length` w/ `exists()`/`count()`
* DEV: Use `exists()` instead of `count() > 0`, `count() === 0`
* DEV: Use `count()`/`exists()` instead of `find().length`
---
 .../tests/acceptance/account-created-test.js  |   6 +-
 .../acceptance/admin-search-log-term-test.js  |   2 +-
 .../acceptance/admin-search-logs-test.js      |   4 +-
 .../acceptance/admin-suspend-user-test.js     |  36 ++-
 .../tests/acceptance/admin-user-index-test.js |  11 +-
 .../acceptance/admin-watched-words-test.js    |   9 +-
 .../tests/acceptance/category-banner-test.js  |   7 +-
 .../acceptance/category-edit-security-test.js |  19 +-
 .../tests/acceptance/click-track-test.js      |  13 +-
 .../tests/acceptance/composer-actions-test.js |  68 ++---
 .../acceptance/composer-hyperlink-test.js     |   3 +-
 .../tests/acceptance/composer-test.js         |  97 ++++---
 .../acceptance/composer-topic-links-test.js   |   4 +-
 .../acceptance/composer-uncategorized-test.js |  12 +-
 .../create-account-user-fields-test.js        |   5 +-
 .../acceptance/create-invite-modal-test.js    |  42 +--
 .../tests/acceptance/do-not-disturb-test.js   |  10 +-
 .../tests/acceptance/flag-post-test.js        |  12 +-
 .../tests/acceptance/forgot-password-test.js  |   2 +-
 .../tests/acceptance/group-index-test.js      |  11 +-
 .../group-manage-categories-test.js           |  14 +-
 .../group-manage-interaction-test.js          |  22 +-
 .../acceptance/group-manage-logs-test.js      |  18 +-
 .../group-manage-membership-test.js           |  80 +++---
 .../acceptance/group-manage-profile-test.js   |  24 +-
 .../acceptance/group-manage-tags-test.js      |  14 +-
 .../tests/acceptance/group-requests-test.js   |   8 +-
 .../discourse/tests/acceptance/group-test.js  |  39 ++-
 .../tests/acceptance/groups-index-test.js     |   4 +-
 .../tests/acceptance/groups-new-test.js       |  24 +-
 .../tests/acceptance/mobile-pan-test.js       |  18 +-
 .../discourse/tests/acceptance/modal-test.js  |  52 ++--
 .../acceptance/notifications-filter-test.js   |   8 +-
 .../tests/acceptance/notifications-test.js    |  14 +-
 .../plugin-outlet-connector-class-test.js     |  14 +-
 .../plugin-outlet-multi-template-test.js      |  16 +-
 .../plugin-outlet-single-template-test.js     |  11 +-
 .../tests/acceptance/preferences-test.js      |  18 +-
 .../acceptance/raw-plugin-outlet-test.js      |  10 +-
 .../discourse/tests/acceptance/review-test.js |  62 ++---
 .../tests/acceptance/search-full-test.js      |  19 +-
 .../tests/acceptance/search-mobile-test.js    |  10 +-
 .../tests/acceptance/share-topic-test.js      |   3 +-
 .../tests/acceptance/shared-drafts-test.js    |  12 +-
 .../tests/acceptance/sign-in-test.js          |   5 +-
 .../tests/acceptance/tag-groups-test.js       |   6 +-
 .../discourse/tests/acceptance/tags-test.js   |  42 ++-
 .../tests/acceptance/topic-anonymous-test.js  |   8 +-
 .../acceptance/topic-quote-button-test.js     |  30 +--
 .../discourse/tests/acceptance/topic-test.js  |  15 +-
 .../tests/acceptance/user-anonymous-test.js   |  14 +-
 .../tests/acceptance/user-bookmarks-test.js   |   2 +-
 .../acceptance/user-drafts-stream-test.js     |  11 +-
 .../user-preferences-interface-test.js        |   6 +-
 .../user-preferences-notifications-test.js    |   4 +-
 .../integration/components/ace-editor-test.js |   7 +-
 .../components/activation-controls-test.js    |   7 +-
 .../components/admin-report-test.js           |   3 +-
 .../integration/components/cook-text-test.js  |   9 +-
 .../integration/components/d-button-test.js   |  57 ++--
 .../integration/components/d-editor-test.js   |  24 +-
 .../group-membership-button-test.js           |  25 +-
 .../components/image-uploader-test.js         |  38 ++-
 .../components/invite-panel-test.js           |   7 +-
 .../components/secret-value-list-test.js      |  24 +-
 .../components/simple-list-test.js            |  34 +--
 .../components/site-header-test.js            |  12 +-
 .../components/slow-mode-info-test.js         |   6 +-
 .../integration/components/text-field-test.js |   5 +-
 .../components/themes-list-item-test.js       |  15 +-
 .../components/themes-list-test.js            |  17 +-
 .../components/user-avatar-flair-test.js      |  27 +-
 .../components/user-selector-test.js          |   9 +-
 .../integration/components/value-list-test.js |  30 ++-
 .../widgets/actions-summary-test.js           |  15 +-
 .../integration/widgets/avatar-flair-test.js  |   9 +-
 .../tests/integration/widgets/button-test.js  |  34 +--
 .../widgets/default-notification-item-test.js |  10 +-
 .../widgets/hamburger-menu-test.js            |  56 ++--
 .../tests/integration/widgets/header-test.js  |  14 +-
 .../integration/widgets/home-logo-test.js     |  41 ++-
 .../integration/widgets/post-links-test.js    |  14 +-
 .../integration/widgets/post-menu-test.js     |  10 +-
 .../integration/widgets/post-stream-test.js   |  19 +-
 .../tests/integration/widgets/post-test.js    | 246 +++++++-----------
 .../integration/widgets/poster-name-test.js   |  23 +-
 .../widgets/quick-access-item-test.js         |   9 +-
 .../widgets/small-user-list-test.js           |   9 +-
 .../widgets/software-update-prompt-test.js    |  11 +-
 .../integration/widgets/topic-status-test.js  |   9 +-
 .../integration/widgets/user-menu-test.js     |  28 +-
 .../widgets/widget-dropdown-test.js           |  12 +-
 .../tests/integration/widgets/widget-test.js  |  38 +--
 .../acceptance/details-button-test.js.es6     |  10 +-
 .../acceptance/poll-breakdown-test.js.es6     |  21 +-
 .../acceptance/poll-pie-chart-test.js.es6     |  10 +-
 .../acceptance/poll-quote-test.js.es6         |   6 +-
 .../widgets/discourse-poll-test.js.es6        |   6 +-
 98 files changed, 976 insertions(+), 1070 deletions(-)

diff --git a/app/assets/javascripts/discourse/tests/acceptance/account-created-test.js b/app/assets/javascripts/discourse/tests/acceptance/account-created-test.js
index fab4de4e04e..0eac23bc6fe 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/account-created-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/account-created-test.js
@@ -60,7 +60,7 @@ acceptance("Account Created", function () {
     await click(".activation-controls .edit-email");
 
     assert.equal(currentRouteName(), "account-created.edit-email");
-    assert.ok(queryAll(".activation-controls .btn-primary:disabled").length);
+    assert.ok(exists(".activation-controls .btn-primary:disabled"));
 
     await click(".activation-controls .edit-cancel");
 
@@ -79,11 +79,11 @@ acceptance("Account Created", function () {
 
     await click(".activation-controls .edit-email");
 
-    assert.ok(queryAll(".activation-controls .btn-primary:disabled").length);
+    assert.ok(exists(".activation-controls .btn-primary:disabled"));
 
     await fillIn(".activate-new-email", "newemail@example.com");
 
-    assert.notOk(queryAll(".activation-controls .btn-primary:disabled").length);
+    assert.notOk(exists(".activation-controls .btn-primary:disabled"));
 
     await click(".activation-controls .btn-primary");
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-search-log-term-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-search-log-term-test.js
index b4a9df1cdeb..6b4d080b683 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/admin-search-log-term-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-search-log-term-test.js
@@ -8,7 +8,7 @@ acceptance("Admin - Search Log Term", function (needs) {
   test("show search log term details", async function (assert) {
     await visit("/admin/logs/search_logs/term?term=ruby");
 
-    assert.ok($("div.search-logs-filter").length, "has the search type filter");
+    assert.ok(exists("div.search-logs-filter"), "has the search type filter");
     assert.ok(exists("canvas.chartjs-render-monitor"), "has graph canvas");
     assert.ok(exists("div.header-search-results"), "has header search results");
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-search-logs-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-search-logs-test.js
index 9b19b29b6be..8d6335451c0 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/admin-search-logs-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-search-logs-test.js
@@ -8,7 +8,7 @@ acceptance("Admin - Search Logs", function (needs) {
   test("show search logs", async function (assert) {
     await visit("/admin/logs/search_logs");
 
-    assert.ok($("table.search-logs-list.grid").length, "has the div class");
+    assert.ok(exists("table.search-logs-list.grid"), "has the div class");
 
     assert.ok(
       exists(".search-logs-list .admin-list-item .col"),
@@ -18,7 +18,7 @@ acceptance("Admin - Search Logs", function (needs) {
     await click(".term a");
 
     assert.ok(
-      $("div.search-logs-filter").length,
+      exists("div.search-logs-filter"),
       "it should show the search log term page"
     );
   });
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 05a8a86dee4..f9821d9043c 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
@@ -1,7 +1,8 @@
 import {
   acceptance,
+  count,
   exists,
-  queryAll,
+  query,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -31,38 +32,39 @@ acceptance("Admin - Suspend User", function (needs) {
     await visit("/admin/users/1234/regular");
     await click(".suspend-user");
 
-    assert.equal(queryAll(".suspend-user-modal:visible").length, 1);
+    assert.equal(count(".suspend-user-modal:visible"), 1);
 
     await click(".d-modal-cancel");
 
-    assert.equal(queryAll(".suspend-user-modal:visible").length, 0);
+    assert.ok(!exists(".suspend-user-modal:visible"));
   });
 
   test("suspend a user - cancel with input", async function (assert) {
     await visit("/admin/users/1234/regular");
     await click(".suspend-user");
 
-    assert.equal(queryAll(".suspend-user-modal:visible").length, 1);
+    assert.equal(count(".suspend-user-modal:visible"), 1);
 
     await fillIn("input.suspend-reason", "for breaking the rules");
     await fillIn(".suspend-message", "this is an email reason why");
 
     await click(".d-modal-cancel");
 
-    assert.equal(queryAll(".bootbox.modal:visible").length, 1);
+    assert.equal(count(".bootbox.modal:visible"), 1);
 
     await click(".modal-footer .btn-default");
-    assert.equal(queryAll(".suspend-user-modal:visible").length, 1);
+    assert.equal(count(".suspend-user-modal:visible"), 1);
     assert.equal(
-      queryAll(".suspend-message")[0].value,
+      query(".suspend-message").value,
       "this is an email reason why"
     );
 
     await click(".d-modal-cancel");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 1);
-    assert.equal(queryAll(".suspend-user-modal:visible").length, 0);
+    assert.equal(count(".bootbox.modal:visible"), 1);
+    assert.ok(!exists(".suspend-user-modal:visible"));
+
     await click(".modal-footer .btn-primary");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 0);
+    assert.ok(!exists(".bootbox.modal:visible"));
   });
 
   test("suspend, then unsuspend a user", async function (assert) {
@@ -76,11 +78,7 @@ acceptance("Admin - Suspend User", function (needs) {
 
     await click(".suspend-user");
 
-    assert.equal(
-      queryAll(".perform-suspend[disabled]").length,
-      1,
-      "disabled by default"
-    );
+    assert.equal(count(".perform-suspend[disabled]"), 1, "disabled by default");
 
     await suspendUntilCombobox.expand();
     await suspendUntilCombobox.selectRowByValue("tomorrow");
@@ -88,15 +86,11 @@ acceptance("Admin - Suspend User", function (needs) {
     await fillIn("input.suspend-reason", "for breaking the rules");
     await fillIn(".suspend-message", "this is an email reason why");
 
-    assert.equal(
-      queryAll(".perform-suspend[disabled]").length,
-      0,
-      "no longer disabled"
-    );
+    assert.ok(!exists(".perform-suspend[disabled]"), "no longer disabled");
 
     await click(".perform-suspend");
 
-    assert.equal(queryAll(".suspend-user-modal:visible").length, 0);
+    assert.ok(!exists(".suspend-user-modal:visible"));
     assert.ok(exists(".suspension-info"));
 
     await click(".unsuspend-user");
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-user-index-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-user-index-test.js
index 731c77c2167..296b8765057 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/admin-user-index-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-user-index-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  exists,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
 import { test } from "qunit";
@@ -81,9 +85,8 @@ acceptance("Admin - User Index", function (needs) {
       "the name should be correct"
     );
 
-    assert.equal(
-      queryAll('.group-chooser span[title="Macdonald"]').length,
-      0,
+    assert.ok(
+      !exists('.group-chooser span[title="Macdonald"]'),
       "group should not be set"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-watched-words-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-watched-words-test.js
index 8d5595381cd..d04bf15984b 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/admin-watched-words-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-watched-words-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -12,7 +13,7 @@ acceptance("Admin - Watched Words", function (needs) {
   test("list words in groups", async function (assert) {
     await visit("/admin/customize/watched_words/action/block");
 
-    assert.equal(find(".admin-watched-words .alert-error").length, 0);
+    assert.ok(!exists(".admin-watched-words .alert-error"));
 
     assert.ok(
       !exists(".watched-words-list"),
@@ -27,7 +28,7 @@ acceptance("Admin - Watched Words", function (needs) {
     await fillIn(".admin-controls .controls input[type=text]", "li");
 
     assert.equal(
-      queryAll(".watched-words-list .watched-word").length,
+      count(".watched-words-list .watched-word"),
       1,
       "When filtering, show words even if checkbox is unchecked."
     );
@@ -83,7 +84,7 @@ acceptance("Admin - Watched Words", function (needs) {
 
     await click("#" + $(word).attr("id"));
 
-    assert.equal(queryAll(".watched-words-list .watched-word").length, 2);
+    assert.equal(count(".watched-words-list .watched-word"), 2);
   });
 
   test("test modal - replace", async function (assert) {
@@ -131,6 +132,6 @@ acceptance("Admin - Watched Words - Bad regular expressions", function (needs) {
 
   test("shows an error message if regex is invalid", async function (assert) {
     await visit("/admin/customize/watched_words/action/block");
-    assert.equal(find(".admin-watched-words .alert-error").length, 1);
+    assert.equal(count(".admin-watched-words .alert-error"), 1);
   });
 });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/category-banner-test.js b/app/assets/javascripts/discourse/tests/acceptance/category-banner-test.js
index ad04cefe06e..28116183515 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/category-banner-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/category-banner-test.js
@@ -1,6 +1,6 @@
 import {
   acceptance,
-  queryAll,
+  count,
   visible,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
@@ -60,8 +60,9 @@ acceptance("Category Banners", function (needs) {
     await click(".modal-footer>.btn-primary");
     assert.ok(!visible(".bootbox.modal"), "it closes the modal");
     assert.ok(visible(".category-read-only-banner"), "it shows a banner");
-    assert.ok(
-      queryAll(".category-read-only-banner .inner").length === 1,
+    assert.equal(
+      count(".category-read-only-banner .inner"),
+      1,
       "it allows staff to embed html in the message"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/category-edit-security-test.js b/app/assets/javascripts/discourse/tests/acceptance/category-edit-security-test.js
index 020aa1fde35..072fba5ae57 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/category-edit-security-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/category-edit-security-test.js
@@ -1,4 +1,9 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import I18n from "I18n";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -68,20 +73,12 @@ acceptance("Category Edit - security", function (needs) {
 
     await click(".row-body .remove-permission");
 
-    assert.equal(
-      queryAll(".row-body").length,
-      0,
-      "removes the permission from the list"
-    );
+    assert.ok(!exists(".row-body"), "removes the permission from the list");
 
     await availableGroups.expand();
     await availableGroups.selectRowByValue("everyone");
 
-    assert.equal(
-      queryAll(".row-body").length,
-      1,
-      "adds back the permission tp the list"
-    );
+    assert.equal(count(".row-body"), 1, "adds back the permission tp the list");
 
     const firstRow = queryAll(".row-body").first();
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/click-track-test.js b/app/assets/javascripts/discourse/tests/acceptance/click-track-test.js
index 9829cc75121..a8beee4272c 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/click-track-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/click-track-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, currentURL, visit } from "@ember/test-helpers";
 import { test } from "qunit";
 
@@ -13,13 +17,10 @@ acceptance("Click Track", function (needs) {
 
   test("Do not track mentions", async function (assert) {
     await visit("/t/internationalization-localization/280");
-    assert.ok(
-      queryAll(".user-card.show").length === 0,
-      "card should not appear"
-    );
+    assert.ok(!exists(".user-card.show"), "card should not appear");
 
     await click('article[data-post-id="3651"] a.mention');
-    assert.ok(queryAll(".user-card.show").length === 1, "card appear");
+    assert.equal(count(".user-card.show"), 1, "card appear");
     assert.equal(currentURL(), "/t/internationalization-localization/280");
     assert.ok(!tracked);
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js
index 36cf8cc5214..f2252193f9c 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
   updateCurrentUser,
@@ -111,23 +112,25 @@ acceptance("Composer Actions", function (needs) {
     );
 
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 0,
+      !exists(".composer-actions svg.d-icon-far-eye-slash"),
       "whisper icon is not visible"
     );
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-share"),
+      1,
       "reply icon is visible"
     );
 
     await composerActions.expand();
     await composerActions.selectRowByValue("toggle_whisper");
 
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-far-eye-slash"),
+      1,
       "whisper icon is visible"
     );
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 0,
+      !exists(".composer-actions svg.d-icon-share"),
       "reply icon is not visible"
     );
   });
@@ -169,7 +172,7 @@ acceptance("Composer Actions", function (needs) {
     const composerActions = selectKit(".composer-actions");
     await composerActions.expand();
     await composerActions.selectRowByValue("reply_as_new_topic");
-    assert.equal(exists(queryAll(".bootbox")), false);
+    assert.ok(!exists(".bootbox"));
   });
 
   test("reply_as_new_group_message", async function (assert) {
@@ -234,7 +237,7 @@ acceptance("Composer Actions", function (needs) {
     await composerActions.selectRowByValue("reply_to_post");
     await composerActions.expand();
 
-    assert.ok(exists(queryAll(".action-title img.avatar")));
+    assert.ok(exists(".action-title img.avatar"));
     assert.equal(
       queryAll(".action-title .user-link").text().trim(),
       "codinghorror"
@@ -291,23 +294,25 @@ acceptance("Composer Actions", function (needs) {
     await click("article#post_3 button.reply");
 
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-anchor").length === 0,
+      !exists(".composer-actions svg.d-icon-anchor"),
       "no-bump icon is not visible"
     );
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-share"),
+      1,
       "reply icon is visible"
     );
 
     await composerActions.expand();
     await composerActions.selectRowByValue("toggle_topic_bump");
 
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-anchor").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-anchor"),
+      1,
       "no-bump icon is visible"
     );
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 0,
+      !exists(".composer-actions svg.d-icon-share"),
       "reply icon is not visible"
     );
 
@@ -315,11 +320,12 @@ acceptance("Composer Actions", function (needs) {
     await composerActions.selectRowByValue("toggle_topic_bump");
 
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-anchor").length === 0,
+      !exists(".composer-actions svg.d-icon-anchor"),
       "no-bump icon is not visible"
     );
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-share"),
+      1,
       "reply icon is visible"
     );
   });
@@ -331,15 +337,16 @@ acceptance("Composer Actions", function (needs) {
     await click("article#post_3 button.reply");
 
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 0,
+      !exists(".composer-actions svg.d-icon-far-eye-slash"),
       "whisper icon is not visible"
     );
     assert.ok(
-      queryAll(".composer-fields .whisper .d-icon-anchor").length === 0,
+      !exists(".composer-fields .whisper .d-icon-anchor"),
       "no-bump icon is not visible"
     );
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-share"),
+      1,
       "reply icon is visible"
     );
 
@@ -348,16 +355,18 @@ acceptance("Composer Actions", function (needs) {
     await composerActions.expand();
     await composerActions.selectRowByValue("toggle_whisper");
 
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-far-eye-slash"),
+      1,
       "whisper icon is visible"
     );
-    assert.ok(
-      queryAll(".composer-fields .no-bump .d-icon-anchor").length === 1,
+    assert.equal(
+      count(".composer-fields .no-bump .d-icon-anchor"),
+      1,
       "no-bump icon is visible"
     );
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-share").length === 0,
+      !exists(".composer-actions svg.d-icon-share"),
       "reply icon is not visible"
     );
   });
@@ -492,12 +501,13 @@ acceptance("Composer Actions With New Topic Draft", function (needs) {
         queryAll("#reply-control .btn-primary.create .d-button-label").text(),
         I18n.t("composer.create_shared_draft")
       );
-      assert.ok(
-        queryAll(".composer-actions svg.d-icon-far-clipboard").length === 1,
+      assert.equal(
+        count(".composer-actions svg.d-icon-far-clipboard"),
+        1,
         "shared draft icon is visible"
       );
 
-      assert.ok(queryAll("#reply-control.composing-shared-draft").length === 1);
+      assert.equal(count("#reply-control.composing-shared-draft"), 1);
       await click(".modal-footer .btn.btn-default");
     } finally {
       toggleCheckDraftPopup(false);
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-hyperlink-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-hyperlink-test.js
index 3cc5eb98eb7..a780a6a27b6 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-hyperlink-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-hyperlink-test.js
@@ -1,6 +1,7 @@
 import {
   acceptance,
   exists,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
@@ -55,7 +56,7 @@ acceptance("Composer - Hyperlink", function (needs) {
       "modal dismissed after cancelling"
     );
 
-    const textarea = queryAll("#reply-control .d-editor-input")[0];
+    const textarea = query("#reply-control .d-editor-input");
     textarea.selectionStart = 0;
     textarea.selectionEnd = 6;
     await click(".d-editor button.link");
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
index 5440a5aa2ce..8839d170b7d 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
@@ -1,7 +1,9 @@
 import {
   acceptance,
+  count,
   exists,
   invisible,
+  query,
   queryAll,
   updateCurrentUser,
   visible,
@@ -91,7 +93,7 @@ acceptance("Composer", function (needs) {
       "the body is now good"
     );
 
-    const textarea = queryAll("#reply-control .d-editor-input")[0];
+    const textarea = query("#reply-control .d-editor-input");
     textarea.selectionStart = textarea.value.length;
     textarea.selectionEnd = textarea.value.length;
 
@@ -284,7 +286,7 @@ acceptance("Composer", function (needs) {
   test("Create an enqueued Reply", async function (assert) {
     await visit("/t/internationalization-localization/280");
 
-    assert.notOk(queryAll(".pending-posts .reviewable-item").length);
+    assert.ok(!exists(".pending-posts .reviewable-item"));
 
     await click("#topic-footer-buttons .btn.create");
     assert.ok(exists(".d-editor-input"), "the composer input is visible");
@@ -305,7 +307,7 @@ acceptance("Composer", function (needs) {
     await click(".modal-footer button");
     assert.ok(invisible(".d-modal"), "the modal can be dismissed");
 
-    assert.ok(queryAll(".pending-posts .reviewable-item").length);
+    assert.ok(exists(".pending-posts .reviewable-item"));
   });
 
   test("Edit the first post", async function (assert) {
@@ -355,7 +357,7 @@ acceptance("Composer", function (needs) {
     await fillIn("#reply-title", "This is the new text for the title");
     await click("#reply-control button.create");
 
-    assert.equal(find(".topic-post.staged").length, 1);
+    assert.equal(count(".topic-post.staged"), 1);
     assert.ok(
       find(".topic-post:nth-of-type(1)")[0].className.includes("staged")
     );
@@ -374,7 +376,7 @@ acceptance("Composer", function (needs) {
     await fillIn("#reply-title", "This is the new text for the title");
     await click("#reply-control button.create");
 
-    assert.equal(find(".topic-post.staged").length, 0);
+    assert.ok(!exists(".topic-post.staged"));
     assert.equal(
       find(".topic-post .cooked")[0].innerText,
       "Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?"
@@ -447,8 +449,9 @@ acceptance("Composer", function (needs) {
     await menu.expand();
     await menu.selectRowByValue("toggleWhisper");
 
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-far-eye-slash"),
+      1,
       "it sets the post type to whisper"
     );
 
@@ -456,7 +459,7 @@ acceptance("Composer", function (needs) {
     await menu.selectRowByValue("toggleWhisper");
 
     assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 0,
+      !exists(".composer-actions svg.d-icon-far-eye-slash"),
       "it removes the whisper mode"
     );
 
@@ -477,37 +480,42 @@ acceptance("Composer", function (needs) {
     await visit("/t/this-is-a-test-topic/9");
     await click(".topic-post:nth-of-type(1) button.reply");
 
-    assert.ok(
-      queryAll("#reply-control.open").length === 1,
+    assert.equal(
+      count("#reply-control.open"),
+      1,
       "it starts in open state by default"
     );
 
     await click(".toggle-fullscreen");
 
-    assert.ok(
-      queryAll("#reply-control.fullscreen").length === 1,
+    assert.equal(
+      count("#reply-control.fullscreen"),
+      1,
       "it expands composer to full screen"
     );
 
     await click(".toggle-fullscreen");
 
-    assert.ok(
-      queryAll("#reply-control.open").length === 1,
+    assert.equal(
+      count("#reply-control.open"),
+      1,
       "it collapses composer to regular size"
     );
 
     await fillIn(".d-editor-input", "This is a dirty reply");
     await click(".toggler");
 
-    assert.ok(
-      queryAll("#reply-control.draft").length === 1,
+    assert.equal(
+      count("#reply-control.draft"),
+      1,
       "it collapses composer to draft bar"
     );
 
     await click(".toggle-fullscreen");
 
-    assert.ok(
-      queryAll("#reply-control.open").length === 1,
+    assert.equal(
+      count("#reply-control.open"),
+      1,
       "from draft, it expands composer back to open state"
     );
   });
@@ -521,8 +529,9 @@ acceptance("Composer", function (needs) {
       "toggleWhisper"
     );
 
-    assert.ok(
-      queryAll(".composer-actions svg.d-icon-far-eye-slash").length === 1,
+    assert.equal(
+      count(".composer-actions svg.d-icon-far-eye-slash"),
+      1,
       "it sets the post type to whisper"
     );
 
@@ -531,7 +540,7 @@ acceptance("Composer", function (needs) {
 
     await click("#create-topic");
     assert.ok(
-      queryAll(".composer-fields .whisper .d-icon-far-eye-slash").length === 0,
+      !exists(".composer-fields .whisper .d-icon-far-eye-slash"),
       "it should reset the state of the composer's model"
     );
 
@@ -551,9 +560,7 @@ acceptance("Composer", function (needs) {
 
     await click(".topic-post:nth-of-type(1) button.reply");
     assert.ok(
-      queryAll(".composer-fields .whisper")
-        .text()
-        .indexOf(I18n.t("composer.unlist")) === -1,
+      !exists(".composer-fields .whisper"),
       "it should reset the state of the composer's model"
     );
   });
@@ -675,17 +682,20 @@ acceptance("Composer", function (needs) {
 
       await fillIn(".d-editor-input", longText);
 
+      assert.ok(
+        exists(
+          '.action-title a[href="/t/internationalization-localization/280"]'
+        ),
+        "the mode should be: reply to post"
+      );
+
       await click("article#post_3 button.reply");
 
       const composerActions = selectKit(".composer-actions");
       await composerActions.expand();
       await composerActions.selectRowByValue("reply_as_private_message");
 
-      assert.equal(
-        queryAll(".modal-body").text(),
-        "",
-        "abandon popup shouldn't come"
-      );
+      assert.ok(!exists(".modal-body"), "abandon popup shouldn't come");
 
       assert.ok(
         queryAll(".d-editor-input").val().includes(longText),
@@ -693,7 +703,7 @@ acceptance("Composer", function (needs) {
       );
 
       assert.ok(
-        queryAll(
+        !exists(
           '.action-title a[href="/t/internationalization-localization/280"]'
         ),
         "mode should have changed"
@@ -786,9 +796,9 @@ acceptance("Composer", function (needs) {
       I18n.t("composer.create_pm"),
       "reply button says Message"
     );
-    assert.ok(
-      queryAll(".save-or-cancel button.create svg.d-icon-envelope").length ===
-        1,
+    assert.equal(
+      count(".save-or-cancel button.create svg.d-icon-envelope"),
+      1,
       "reply button has envelope icon"
     );
   });
@@ -803,9 +813,9 @@ acceptance("Composer", function (needs) {
       I18n.t("composer.save_edit"),
       "save button says Save Edit"
     );
-    assert.ok(
-      queryAll(".save-or-cancel button.create svg.d-icon-pencil-alt").length ===
-        1,
+    assert.equal(
+      count(".save-or-cancel button.create svg.d-icon-pencil-alt"),
+      1,
       "save button has pencil icon"
     );
   });
@@ -844,8 +854,9 @@ acceptance("Composer", function (needs) {
 
     await fillIn(".d-editor-input", uploads.join("\n"));
 
-    assert.ok(
-      queryAll(".button-wrapper").length === 10,
+    assert.equal(
+      count(".button-wrapper"),
+      10,
       "it adds correct amount of scaling button groups"
     );
 
@@ -925,8 +936,8 @@ acceptance("Composer", function (needs) {
     );
 
     assert.ok(
-      queryAll("script").length === 0,
-      "it does not unescapes script tags in code blocks"
+      !exists("script"),
+      "it does not unescape script tags in code blocks"
     );
   });
 
@@ -947,13 +958,13 @@ acceptance("Composer", function (needs) {
     );
 
     await fillIn(".d-editor-input", "[](https://discourse.org)");
-    assert.equal(find(".composer-popup").length, 0);
+    assert.ok(!exists(".composer-popup"));
 
     await fillIn(".d-editor-input", "[quote][](https://github.com)[/quote]");
-    assert.equal(find(".composer-popup").length, 0);
+    assert.ok(!exists(".composer-popup"));
 
     await fillIn(".d-editor-input", "[](https://github.com)");
-    assert.equal(find(".composer-popup").length, 1);
+    assert.equal(count(".composer-popup"), 1);
   });
 
   test("Shows the 'group_mentioned' notice", async function (assert) {
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-topic-links-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-topic-links-test.js
index 02307c26985..67587ee9c86 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-topic-links-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-topic-links-test.js
@@ -181,7 +181,7 @@ acceptance(
       await visit("/");
       await click("#create-topic");
       assert.ok(
-        queryAll(".d-editor-textarea-wrapper.disabled").length,
+        exists(".d-editor-textarea-wrapper.disabled"),
         "textarea is disabled"
       );
       await fillIn("#reply-title", "http://www.example.com/has-title.html");
@@ -199,7 +199,7 @@ acceptance(
         "title is from the oneboxed article"
       );
       assert.ok(
-        queryAll(".d-editor-textarea-wrapper.disabled").length === 0,
+        !exists(".d-editor-textarea-wrapper.disabled"),
         "textarea is enabled"
       );
     });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-uncategorized-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-uncategorized-test.js
index f9b8c1be32e..7d34f5fd0bb 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-uncategorized-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-uncategorized-test.js
@@ -1,8 +1,4 @@
-import {
-  acceptance,
-  exists,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
 import { test } from "qunit";
@@ -39,7 +35,7 @@ acceptance(
       await categoryChooser.selectRowByValue(2);
 
       assert.ok(
-        queryAll(".d-editor-textarea-wrapper.disabled").length === 0,
+        !exists(".d-editor-textarea-wrapper.disabled"),
         "textarea is enabled"
       );
 
@@ -48,7 +44,7 @@ acceptance(
       await categoryChooser.selectRowByIndex(0);
 
       assert.ok(
-        queryAll(".d-editor-textarea-wrapper.disabled").length === 0,
+        !exists(".d-editor-textarea-wrapper.disabled"),
         "textarea is still enabled"
       );
     });
@@ -91,7 +87,7 @@ acceptance(
         "category errors are hidden by default"
       );
       assert.ok(
-        queryAll(".d-editor-textarea-wrapper.disabled").length === 0,
+        !exists(".d-editor-textarea-wrapper.disabled"),
         "textarea is enabled"
       );
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/create-account-user-fields-test.js b/app/assets/javascripts/discourse/tests/acceptance/create-account-user-fields-test.js
index c85cb272c5c..02adf36a8de 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/create-account-user-fields-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/create-account-user-fields-test.js
@@ -1,6 +1,7 @@
 import {
   acceptance,
   exists,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
@@ -62,12 +63,12 @@ acceptance("Create Account - User Fields", function (needs) {
     );
 
     await click(".modal-footer .btn-primary");
-    assert.equal(queryAll("#modal-alert")[0].style.display, "");
+    assert.equal(query("#modal-alert").style.display, "");
 
     await fillIn(".user-field input[type=text]:nth-of-type(1)", "Barky");
     await click(".user-field input[type=checkbox]");
 
     await click(".modal-footer .btn-primary");
-    assert.equal(queryAll("#modal-alert")[0].style.display, "none");
+    assert.equal(query("#modal-alert").style.display, "none");
   });
 });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/create-invite-modal-test.js b/app/assets/javascripts/discourse/tests/acceptance/create-invite-modal-test.js
index 80afabea8e9..770292118e3 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/create-invite-modal-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/create-invite-modal-test.js
@@ -1,5 +1,9 @@
 import { click, fillIn, visit } from "@ember/test-helpers";
-import { acceptance } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+} from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
 
 acceptance("Invites - Create & Edit Invite Modal", function (needs) {
@@ -52,18 +56,9 @@ acceptance("Invites - Create & Edit Invite Modal", function (needs) {
     );
 
     await click(".modal-footer .show-advanced");
-    await assert.ok(
-      find(".invite-to-groups").length > 0,
-      "shows advanced options"
-    );
-    await assert.ok(
-      find(".invite-to-topic").length > 0,
-      "shows advanced options"
-    );
-    await assert.ok(
-      find(".invite-expires-at").length > 0,
-      "shows advanced options"
-    );
+    await assert.ok(exists(".invite-to-groups"), "shows advanced options");
+    await assert.ok(exists(".invite-to-topic"), "shows advanced options");
+    await assert.ok(exists(".invite-expires-at"), "shows advanced options");
 
     await click(".modal-close");
     assert.ok(deleted, "deletes the invite if not saved");
@@ -73,17 +68,11 @@ acceptance("Invites - Create & Edit Invite Modal", function (needs) {
     await visit("/u/eviltrout/invited/pending");
     await click(".invite-controls .btn:first-child");
 
-    assert.ok(
-      find("tbody tr").length === 0,
-      "does not show invite before saving"
-    );
+    assert.ok(!exists("tbody tr"), "does not show invite before saving");
 
     await click(".btn-primary");
 
-    assert.ok(
-      find("tbody tr").length === 1,
-      "adds invite to list after saving"
-    );
+    assert.equal(count("tbody tr"), 1, "adds invite to list after saving");
 
     await click(".modal-close");
     assert.notOk(deleted, "does not delete invite on close");
@@ -138,10 +127,7 @@ acceptance("Invites - Link Invites", function (needs) {
     await visit("/u/eviltrout/invited/pending");
     await click(".invite-controls .btn:first-child");
 
-    assert.ok(
-      find("#invite-max-redemptions").length,
-      "shows max redemptions field"
-    );
+    assert.ok(exists("#invite-max-redemptions"), "shows max redemptions field");
   });
 });
 
@@ -180,10 +166,10 @@ acceptance("Invites - Email Invites", function (needs) {
     await visit("/u/eviltrout/invited/pending");
     await click(".invite-controls .btn:first-child");
 
-    assert.ok(find("#invite-email").length, "shows email field");
+    assert.ok(exists("#invite-email"), "shows email field");
     await fillIn("#invite-email", "test@example.com");
 
-    assert.ok(find(".save-invite").length, "shows save without email button");
+    assert.ok(exists(".save-invite"), "shows save without email button");
     await click(".save-invite");
     assert.ok(
       lastRequest.requestBody.indexOf("skip_email=true") !== -1,
@@ -191,7 +177,7 @@ acceptance("Invites - Email Invites", function (needs) {
     );
 
     await fillIn("#invite-email", "test2@example.com");
-    assert.ok(find(".send-invite").length, "shows save and send email button");
+    assert.ok(exists(".send-invite"), "shows save and send email button");
     await click(".send-invite");
     assert.ok(
       lastRequest.requestBody.indexOf("send_email=true") !== -1,
diff --git a/app/assets/javascripts/discourse/tests/acceptance/do-not-disturb-test.js b/app/assets/javascripts/discourse/tests/acceptance/do-not-disturb-test.js
index fea8383deed..7f376eafbde 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/do-not-disturb-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/do-not-disturb-test.js
@@ -1,6 +1,7 @@
 import {
   acceptance,
   exists,
+  query,
   queryAll,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -37,7 +38,7 @@ acceptance("Do not disturb", function (needs) {
     await click(tiles[0]);
 
     assert.ok(
-      queryAll(".do-not-disturb-modal")[0].style.display === "none",
+      query(".do-not-disturb-modal").style.display === "none",
       "modal is hidden"
     );
 
@@ -55,15 +56,12 @@ acceptance("Do not disturb", function (needs) {
     await visit("/");
     await click(".header-dropdown-toggle.current-user");
     await click(".menu-links-row .user-preferences-link");
-    assert.equal(
-      queryAll(".do-not-disturb .relative-date")[0].textContent,
-      "1h"
-    );
+    assert.equal(query(".do-not-disturb .relative-date").textContent, "1h");
 
     await click(".do-not-disturb");
 
     assert.ok(
-      queryAll(".do-not-disturb-background").length === 0,
+      !exists(".do-not-disturb-background"),
       "The active moon icons are removed"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js b/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js
index 253a7b28fae..ef40fe01c4e 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js
@@ -1,7 +1,7 @@
 import {
   acceptance,
+  count,
   exists,
-  queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -115,7 +115,7 @@ acceptance("flagging", function (needs) {
     await silenceUntilCombobox.selectRowByValue("tomorrow");
     await fillIn(".silence-reason", "for breaking the rules");
     await click(".perform-silence");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 0);
+    assert.ok(!exists(".bootbox.modal:visible"));
   });
 
   test("Gets dismissable warning from canceling incomplete silence from take action", async function (assert) {
@@ -130,16 +130,16 @@ acceptance("flagging", function (needs) {
     await silenceUntilCombobox.selectRowByValue("tomorrow");
     await fillIn(".silence-reason", "for breaking the rules");
     await click(".d-modal-cancel");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 1);
+    assert.equal(count(".bootbox.modal:visible"), 1);
 
     await click(".modal-footer .btn-default");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 0);
+    assert.ok(!exists(".bootbox.modal:visible"));
     assert.ok(exists(".silence-user-modal"), "it shows the silence modal");
 
     await click(".d-modal-cancel");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 1);
+    assert.equal(count(".bootbox.modal:visible"), 1);
 
     await click(".modal-footer .btn-primary");
-    assert.equal(queryAll(".bootbox.modal:visible").length, 0);
+    assert.ok(!exists(".bootbox.modal:visible"));
   });
 });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/forgot-password-test.js b/app/assets/javascripts/discourse/tests/acceptance/forgot-password-test.js
index 92df2514a1c..0905fd351df 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/forgot-password-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/forgot-password-test.js
@@ -58,7 +58,7 @@ acceptance("Forgot password", function (needs) {
     await click(".forgot-password-reset");
 
     assert.notOk(
-      exists(queryAll(".alert-error")),
+      exists(".alert-error"),
       "it should remove the flash error when succeeding"
     );
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-index-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-index-test.js
index 93a463a8d7a..674ddce0d52 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-index-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-index-test.js
@@ -1,6 +1,7 @@
 import {
   acceptance,
   count,
+  exists,
   queryAll,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -17,10 +18,10 @@ acceptance("Group Members - Anonymous", function () {
       count(".avatar-flair .d-icon-adjust") === 1,
       "it displays the group's avatar flair"
     );
-    assert.ok(count(".group-members tr") > 0, "it lists group members");
+    assert.ok(exists(".group-members tr"), "it lists group members");
 
     assert.ok(
-      count(".group-member-dropdown") === 0,
+      !exists(".group-member-dropdown"),
       "it does not allow anon user to manage group members"
     );
 
@@ -48,7 +49,7 @@ acceptance("Group Members", function (needs) {
     await click(".group-members-add");
 
     assert.equal(
-      queryAll("#group-add-members-user-selector").length,
+      count("#group-add-members-user-selector"),
       1,
       "it should display the add members modal"
     );
@@ -58,7 +59,7 @@ acceptance("Group Members", function (needs) {
     await visit("/g/discourse");
 
     assert.ok(
-      count(".group-member-dropdown") > 0,
+      exists(".group-member-dropdown"),
       "it allows admin user to manage group members"
     );
 
@@ -72,7 +73,7 @@ acceptance("Group Members", function (needs) {
   test("Shows bulk actions", async function (assert) {
     await visit("/g/discourse");
 
-    assert.ok(count("button.bulk-select") > 0);
+    assert.ok(exists("button.bulk-select"));
     await click("button.bulk-select");
 
     await click(queryAll("input.bulk-select")[0]);
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-manage-categories-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-manage-categories-test.js
index 573b606da3d..62187603a4a 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-manage-categories-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-manage-categories-test.js
@@ -1,7 +1,7 @@
 import {
   acceptance,
   count,
-  queryAll,
+  exists,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
@@ -12,7 +12,7 @@ acceptance("Managing Group Category Notification Defaults", function () {
     await visit("/g/discourse/manage/categories");
 
     assert.ok(
-      count(".group-members tr") > 0,
+      exists(".group-members tr"),
       "it should redirect to members page for an anonymous user"
     );
   });
@@ -23,8 +23,9 @@ acceptance("Managing Group Category Notification Defaults", function (needs) {
   test("As an admin", async function (assert) {
     await visit("/g/discourse/manage/categories");
 
-    assert.ok(
-      queryAll(".groups-notifications-form .category-selector").length === 5,
+    assert.equal(
+      count(".groups-notifications-form .category-selector"),
+      5,
       "it should display category inputs"
     );
   });
@@ -34,8 +35,9 @@ acceptance("Managing Group Category Notification Defaults", function (needs) {
 
     await visit("/g/discourse/manage/categories");
 
-    assert.ok(
-      queryAll(".groups-notifications-form .category-selector").length === 5,
+    assert.equal(
+      count(".groups-notifications-form .category-selector"),
+      5,
       "it should display category inputs"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-manage-interaction-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-manage-interaction-test.js
index e5bf10edbbb..67797ae306d 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-manage-interaction-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-manage-interaction-test.js
@@ -1,6 +1,6 @@
 import {
   acceptance,
-  queryAll,
+  count,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
@@ -20,31 +20,31 @@ acceptance("Managing Group Interaction Settings", function (needs) {
     await visit("/g/alternative-group/manage/interaction");
 
     assert.equal(
-      queryAll(".groups-form-visibility-level").length,
+      count(".groups-form-visibility-level"),
       1,
       "it should display visibility level selector"
     );
 
     assert.equal(
-      queryAll(".groups-form-mentionable-level").length,
+      count(".groups-form-mentionable-level"),
       1,
       "it should display mentionable level selector"
     );
 
     assert.equal(
-      queryAll(".groups-form-messageable-level").length,
+      count(".groups-form-messageable-level"),
       1,
       "it should display messageable level selector"
     );
 
     assert.equal(
-      queryAll(".groups-form-incoming-email").length,
+      count(".groups-form-incoming-email"),
       1,
       "it should display incoming email input"
     );
 
     assert.equal(
-      queryAll(".groups-form-default-notification-level").length,
+      count(".groups-form-default-notification-level"),
       1,
       "it should display default notification level input"
     );
@@ -60,31 +60,31 @@ acceptance("Managing Group Interaction Settings", function (needs) {
     await visit("/g/discourse/manage/interaction");
 
     assert.equal(
-      queryAll(".groups-form-visibility-level").length,
+      count(".groups-form-visibility-level"),
       0,
       "it should not display visibility level selector"
     );
 
     assert.equal(
-      queryAll(".groups-form-mentionable-level").length,
+      count(".groups-form-mentionable-level"),
       1,
       "it should display mentionable level selector"
     );
 
     assert.equal(
-      queryAll(".groups-form-messageable-level").length,
+      count(".groups-form-messageable-level"),
       1,
       "it should display messageable level selector"
     );
 
     assert.equal(
-      queryAll(".groups-form-incoming-email").length,
+      count(".groups-form-incoming-email"),
       0,
       "it should not display incoming email input"
     );
 
     assert.equal(
-      queryAll(".groups-form-default-notification-level").length,
+      count(".groups-form-default-notification-level"),
       1,
       "it should display default notification level input"
     );
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-manage-logs-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-manage-logs-test.js
index 6ae37982a54..4d1046a019c 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-manage-logs-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-manage-logs-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  query,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import { test } from "qunit";
 
@@ -95,14 +99,16 @@ acceptance("Group logs", function (needs) {
 
   test("Browsing group logs", async function (assert) {
     await visit("/g/snorlax/manage/logs");
-    assert.ok(
-      queryAll("tr.group-manage-logs-row").length === 2,
+    assert.equal(
+      count("tr.group-manage-logs-row"),
+      2,
       "it should display the right number of logs"
     );
 
-    await click(queryAll(".group-manage-logs-row button")[0]);
-    assert.ok(
-      queryAll("tr.group-manage-logs-row").length === 1,
+    await click(query(".group-manage-logs-row button"));
+    assert.equal(
+      count("tr.group-manage-logs-row"),
+      1,
       "it should display the right number of logs"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-manage-membership-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-manage-membership-test.js
index afef5e560c2..45e0ae73ac3 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-manage-membership-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-manage-membership-test.js
@@ -1,6 +1,7 @@
 import {
   acceptance,
-  queryAll,
+  count,
+  exists,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
@@ -15,61 +16,70 @@ acceptance("Managing Group Membership", function (needs) {
 
     await visit("/g/alternative-group/manage/membership");
 
-    assert.ok(
-      queryAll('label[for="automatic_membership"]').length === 1,
+    assert.equal(
+      count('label[for="automatic_membership"]'),
+      1,
       "it should display automatic membership label"
     );
 
-    assert.ok(
-      queryAll(".groups-form-primary-group").length === 1,
+    assert.equal(
+      count(".groups-form-primary-group"),
+      1,
       "it should display set as primary group checkbox"
     );
 
-    assert.ok(
-      queryAll(".groups-form-grant-trust-level").length === 1,
+    assert.equal(
+      count(".groups-form-grant-trust-level"),
+      1,
       "it should display grant trust level selector"
     );
 
-    assert.ok(
-      queryAll(".group-form-public-admission").length === 1,
+    assert.equal(
+      count(".group-form-public-admission"),
+      1,
       "it should display group public admission input"
     );
 
-    assert.ok(
-      queryAll(".group-form-public-exit").length === 1,
+    assert.equal(
+      count(".group-form-public-exit"),
+      1,
       "it should display group public exit input"
     );
 
-    assert.ok(
-      queryAll(".group-form-allow-membership-requests").length === 1,
+    assert.equal(
+      count(".group-form-allow-membership-requests"),
+      1,
       "it should display group allow_membership_request input"
     );
 
-    assert.ok(
-      queryAll(".group-form-allow-membership-requests[disabled]").length === 1,
+    assert.equal(
+      count(".group-form-allow-membership-requests[disabled]"),
+      1,
       "it should disable group allow_membership_request input"
     );
 
-    assert.ok(
-      queryAll(".group-flair-inputs").length === 1,
+    assert.equal(
+      count(".group-flair-inputs"),
+      1,
       "it should display avatar flair inputs"
     );
 
     await click(".group-form-public-admission");
     await click(".group-form-allow-membership-requests");
 
-    assert.ok(
-      queryAll(".group-form-public-admission[disabled]").length === 1,
+    assert.equal(
+      count(".group-form-public-admission[disabled]"),
+      1,
       "it should disable group public admission input"
     );
 
     assert.ok(
-      queryAll(".group-form-public-exit[disabled]").length === 0,
+      !exists(".group-form-public-exit[disabled]"),
       "it should not disable group public exit input"
     );
 
     assert.equal(
-      queryAll(".group-form-membership-request-template").length,
+      count(".group-form-membership-request-template"),
       1,
       "it should display the membership request template field"
     );
@@ -90,42 +100,46 @@ acceptance("Managing Group Membership", function (needs) {
     await visit("/g/discourse/manage/membership");
 
     assert.ok(
-      queryAll('label[for="automatic_membership"]').length === 0,
+      !exists('label[for="automatic_membership"]'),
       "it should not display automatic membership label"
     );
 
     assert.ok(
-      queryAll(".groups-form-automatic-membership-retroactive").length === 0,
+      !exists(".groups-form-automatic-membership-retroactive"),
       "it should not display automatic membership retroactive checkbox"
     );
 
     assert.ok(
-      queryAll(".groups-form-primary-group").length === 0,
+      !exists(".groups-form-primary-group"),
       "it should not display set as primary group checkbox"
     );
 
     assert.ok(
-      queryAll(".groups-form-grant-trust-level").length === 0,
+      !exists(".groups-form-grant-trust-level"),
       "it should not display grant trust level selector"
     );
 
-    assert.ok(
-      queryAll(".group-form-public-admission").length === 1,
+    assert.equal(
+      count(".group-form-public-admission"),
+      1,
       "it should display group public admission input"
     );
 
-    assert.ok(
-      queryAll(".group-form-public-exit").length === 1,
+    assert.equal(
+      count(".group-form-public-exit"),
+      1,
       "it should display group public exit input"
     );
 
-    assert.ok(
-      queryAll(".group-form-allow-membership-requests").length === 1,
+    assert.equal(
+      count(".group-form-allow-membership-requests"),
+      1,
       "it should display group allow_membership_request input"
     );
 
-    assert.ok(
-      queryAll(".group-form-allow-membership-requests[disabled]").length === 1,
+    assert.equal(
+      count(".group-form-allow-membership-requests[disabled]"),
+      1,
       "it should disable group allow_membership_request input"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-manage-profile-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-manage-profile-test.js
index 7a0ced08025..70fda0422fa 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-manage-profile-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-manage-profile-test.js
@@ -1,7 +1,7 @@
 import {
   acceptance,
   count,
-  queryAll,
+  exists,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
@@ -12,7 +12,7 @@ acceptance("Managing Group Profile", function () {
     await visit("/g/discourse/manage/profile");
 
     assert.ok(
-      count(".group-members tr") > 0,
+      exists(".group-members tr"),
       "it should redirect to members page for an anonymous user"
     );
   });
@@ -24,16 +24,19 @@ acceptance("Managing Group Profile", function (needs) {
   test("As an admin", async function (assert) {
     await visit("/g/discourse/manage/profile");
 
-    assert.ok(
-      queryAll(".group-form-bio").length === 1,
+    assert.equal(
+      count(".group-form-bio"),
+      1,
       "it should display group bio input"
     );
-    assert.ok(
-      queryAll(".group-form-name").length === 1,
+    assert.equal(
+      count(".group-form-name"),
+      1,
       "it should display group name input"
     );
-    assert.ok(
-      queryAll(".group-form-full-name").length === 1,
+    assert.equal(
+      count(".group-form-full-name"),
+      1,
       "it should display group full name input"
     );
   });
@@ -47,9 +50,8 @@ acceptance("Managing Group Profile", function (needs) {
 
     await visit("/g/discourse/manage/profile");
 
-    assert.equal(
-      queryAll(".group-form-name").length,
-      0,
+    assert.ok(
+      !exists(".group-form-name"),
       "it should not display group name input"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-manage-tags-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-manage-tags-test.js
index f309023e6d3..6b235c88d00 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-manage-tags-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-manage-tags-test.js
@@ -1,7 +1,7 @@
 import {
   acceptance,
   count,
-  queryAll,
+  exists,
   updateCurrentUser,
 } from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
@@ -12,7 +12,7 @@ acceptance("Managing Group Tag Notification Defaults", function () {
     await visit("/g/discourse/manage/tags");
 
     assert.ok(
-      count(".group-members tr") > 0,
+      exists(".group-members tr"),
       "it should redirect to members page for an anonymous user"
     );
   });
@@ -24,8 +24,9 @@ acceptance("Managing Group Tag Notification Defaults", function (needs) {
   test("As an admin", async function (assert) {
     await visit("/g/discourse/manage/tags");
 
-    assert.ok(
-      queryAll(".groups-notifications-form .tag-chooser").length === 5,
+    assert.equal(
+      count(".groups-notifications-form .tag-chooser"),
+      5,
       "it should display tag inputs"
     );
   });
@@ -35,8 +36,9 @@ acceptance("Managing Group Tag Notification Defaults", function (needs) {
 
     await visit("/g/discourse/manage/tags");
 
-    assert.ok(
-      queryAll(".groups-notifications-form .tag-chooser").length === 5,
+    assert.equal(
+      count(".groups-notifications-form .tag-chooser"),
+      5,
       "it should display tag inputs"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js
index 3c91365f64a..fbfca35e2dd 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-requests-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import { test } from "qunit";
 
@@ -85,7 +89,7 @@ acceptance("Group Requests", function (needs) {
   test("Group Requests", async function (assert) {
     await visit("/g/Macdonald/requests");
 
-    assert.equal(queryAll(".group-members tr").length, 2);
+    assert.equal(count(".group-members tr"), 2);
     assert.equal(
       queryAll(".group-members tr:first-child td:nth-child(1)")
         .text()
diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-test.js
index e1724299041..3ba3ea4a7c6 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/group-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/group-test.js
@@ -28,15 +28,14 @@ acceptance("Group - Anonymous", function (needs) {
   test("Anonymous Viewing Group", async function (assert) {
     await visit("/g/discourse");
 
-    assert.equal(
-      count(".nav-pills li a[title='Messages']"),
-      0,
+    assert.ok(
+      !exists(".nav-pills li a[title='Messages']"),
       "it does not show group messages navigation link"
     );
 
     await click(".nav-pills li a[title='Activity']");
 
-    assert.ok(count(".user-stream-item") > 0, "it lists stream items");
+    assert.ok(exists(".user-stream-item"), "it lists stream items");
 
     await click(".activity-nav li a[href='/g/discourse/activity/topics']");
 
@@ -45,16 +44,16 @@ acceptance("Group - Anonymous", function (needs) {
 
     await click(".activity-nav li a[href='/g/discourse/activity/mentions']");
 
-    assert.ok(count(".user-stream-item") > 0, "it lists stream items");
+    assert.ok(exists(".user-stream-item"), "it lists stream items");
     assert.ok(
-      queryAll(".nav-pills li a[title='Edit Group']").length === 0,
+      !exists(".nav-pills li a[title='Edit Group']"),
       "it should not show messages tab if user is not admin"
     );
     assert.ok(
-      queryAll(".nav-pills li a[title='Logs']").length === 0,
+      !exists(".nav-pills li a[title='Logs']"),
       "it should not show Logs tab if user is not admin"
     );
-    assert.ok(count(".user-stream-item") > 0, "it lists stream items");
+    assert.ok(exists(".user-stream-item"), "it lists stream items");
 
     const groupDropdown = selectKit(".group-dropdown");
     await groupDropdown.expand();
@@ -73,9 +72,8 @@ acceptance("Group - Anonymous", function (needs) {
 
     await groupDropdown.expand();
 
-    assert.equal(
-      queryAll(".group-dropdown-filter").length,
-      0,
+    assert.ok(
+      !exists(".group-dropdown-filter"),
       "it should not display the default header"
     );
   });
@@ -83,9 +81,8 @@ acceptance("Group - Anonymous", function (needs) {
   test("Anonymous Viewing Automatic Group", async function (assert) {
     await visit("/g/moderators");
 
-    assert.equal(
-      count(".nav-pills li a[title='Manage']"),
-      0,
+    assert.ok(
+      !exists(".nav-pills li a[title='Manage']"),
       "it does not show group messages navigation link"
     );
   });
@@ -214,7 +211,7 @@ acceptance("Group - Authenticated", function (needs) {
 
     await click(".group-message-button");
 
-    assert.ok(count("#reply-control") === 1, "it opens the composer");
+    assert.equal(count("#reply-control"), 1, "it opens the composer");
     assert.equal(
       queryAll("#private-message-users .selected-name").text().trim(),
       "discourse",
@@ -249,8 +246,9 @@ acceptance("Group - Authenticated", function (needs) {
   test("Admin Viewing Group", async function (assert) {
     await visit("/g/discourse");
 
-    assert.ok(
-      queryAll(".nav-pills li a[title='Manage']").length === 1,
+    assert.equal(
+      count(".nav-pills li a[title='Manage']"),
+      1,
       "it should show manage group tab if user is admin"
     );
 
@@ -281,15 +279,16 @@ acceptance("Group - Authenticated", function (needs) {
   test("Moderator Viewing Group", async function (assert) {
     await visit("/g/alternative-group");
 
-    assert.ok(
-      queryAll(".nav-pills li a[title='Manage']").length === 1,
+    assert.equal(
+      count(".nav-pills li a[title='Manage']"),
+      1,
       "it should show manage group tab if user can_admin_group"
     );
 
     await click(".group-members-add.btn");
 
     assert.ok(
-      queryAll(".group-add-members-modal .group-add-members-make-owner"),
+      exists(".group-add-members-modal .group-add-members-make-owner"),
       "it allows moderators to set group owners"
     );
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/groups-index-test.js b/app/assets/javascripts/discourse/tests/acceptance/groups-index-test.js
index e1bc069f20e..22a425a1892 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/groups-index-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/groups-index-test.js
@@ -18,12 +18,12 @@ acceptance("Groups", function () {
 
     assert.equal(count(".group-box"), 2, "it displays visible groups");
     assert.equal(
-      queryAll(".group-index-join").length,
+      count(".group-index-join"),
       1,
       "it shows button to join group"
     );
     assert.equal(
-      queryAll(".group-index-request").length,
+      count(".group-index-request"),
       1,
       "it shows button to request for group membership"
     );
diff --git a/app/assets/javascripts/discourse/tests/acceptance/groups-new-test.js b/app/assets/javascripts/discourse/tests/acceptance/groups-new-test.js
index 421add93b56..c79299dcdc8 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/groups-new-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/groups-new-test.js
@@ -1,4 +1,9 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
 import I18n from "I18n";
 import { test } from "qunit";
@@ -7,9 +12,8 @@ acceptance("New Group - Anonymous", function () {
   test("As an anon user", async function (assert) {
     await visit("/g");
 
-    assert.equal(
-      queryAll(".groups-header-new").length,
-      0,
+    assert.ok(
+      !exists(".groups-header-new"),
       "it should not display the button to create a group"
     );
   });
@@ -22,7 +26,7 @@ acceptance("New Group - Authenticated", function (needs) {
     await click(".groups-header-new");
 
     assert.equal(
-      queryAll(".group-form-save[disabled]").length,
+      count(".group-form-save[disabled]"),
       1,
       "save button should be disabled"
     );
@@ -35,8 +39,9 @@ acceptance("New Group - Authenticated", function (needs) {
       "it should show the right validation tooltip"
     );
 
-    assert.ok(
-      queryAll(".group-form-save:disabled").length === 1,
+    assert.equal(
+      count(".group-form-save:disabled"),
+      1,
       "it should disable the save button"
     );
 
@@ -69,9 +74,8 @@ acceptance("New Group - Authenticated", function (needs) {
 
     await click(".group-form-public-admission");
 
-    assert.equal(
-      queryAll("groups-new-allow-membership-requests").length,
-      0,
+    assert.ok(
+      !exists("groups-new-allow-membership-requests"),
       "it should disable the membership requests checkbox"
     );
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js b/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js
index 837e303e80e..ff78f547796 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/mobile-pan-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, triggerEvent, visit } from "@ember/test-helpers";
 import { test } from "qunit";
 
@@ -42,6 +46,7 @@ async function triggerSwipeStart(touchTarget) {
   });
   return touchStart;
 }
+
 async function triggerSwipeMove({ x, y, touchTarget }) {
   const touch = new Touch({
     identifier: "test",
@@ -54,6 +59,7 @@ async function triggerSwipeMove({ x, y, touchTarget }) {
     targetTouches: [touch],
   });
 }
+
 async function triggerSwipeEnd({ x, y, touchTarget }) {
   const touch = new Touch({
     identifier: "test",
@@ -70,6 +76,7 @@ async function triggerSwipeEnd({ x, y, touchTarget }) {
 acceptance("Mobile - menu swipes", function (needs) {
   needs.mobileView();
   needs.user();
+
   test("swipe to close hamburger", async function (assert) {
     await visit("/");
     await click(".hamburger-dropdown");
@@ -81,7 +88,7 @@ acceptance("Mobile - menu swipes", function (needs) {
     await triggerSwipeEnd(swipe);
 
     assert.ok(
-      queryAll(".panel-body").length === 0,
+      !exists(".panel-body"),
       "it should close hamburger on a left swipe"
     );
   });
@@ -98,8 +105,9 @@ acceptance("Mobile - menu swipes", function (needs) {
     await triggerSwipeMove(swipe);
     await triggerSwipeEnd(swipe);
 
-    assert.ok(
-      queryAll(".panel-body").length === 1,
+    assert.equal(
+      count(".panel-body"),
+      1,
       "it should re-open hamburger on a right swipe"
     );
   });
@@ -115,7 +123,7 @@ acceptance("Mobile - menu swipes", function (needs) {
     await triggerSwipeEnd(swipe);
 
     assert.ok(
-      queryAll(".panel-body").length === 0,
+      !exists(".panel-body"),
       "it should close user menu on a left swipe"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/modal-test.js b/app/assets/javascripts/discourse/tests/acceptance/modal-test.js
index f4ca0364a27..e1afdc715f3 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/modal-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/modal-test.js
@@ -1,6 +1,8 @@
 import {
   acceptance,
   controllerFor,
+  count,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, triggerKeyEvent, visit } from "@ember/test-helpers";
@@ -31,35 +33,26 @@ acceptance("Modal", function (needs) {
   skip("modal", async function (assert) {
     await visit("/");
 
-    assert.ok(
-      queryAll(".d-modal:visible").length === 0,
-      "there is no modal at first"
-    );
+    assert.ok(!exists(".d-modal:visible"), "there is no modal at first");
 
     await click(".login-button");
-    assert.ok(queryAll(".d-modal:visible").length === 1, "modal should appear");
+    assert.equal(count(".d-modal:visible"), 1, "modal should appear");
 
     let controller = controllerFor("modal");
     assert.equal(controller.name, "login");
 
     await click(".modal-outer-container");
     assert.ok(
-      queryAll(".d-modal:visible").length === 0,
+      !exists(".d-modal:visible"),
       "modal should disappear when you click outside"
     );
     assert.equal(controller.name, null);
 
     await click(".login-button");
-    assert.ok(
-      queryAll(".d-modal:visible").length === 1,
-      "modal should reappear"
-    );
+    assert.equal(count(".d-modal:visible"), 1, "modal should reappear");
 
     await triggerKeyEvent("#main-outlet", "keyup", 27);
-    assert.ok(
-      queryAll(".d-modal:visible").length === 0,
-      "ESC should close the modal"
-    );
+    assert.ok(!exists(".d-modal:visible"), "ESC should close the modal");
 
     Ember.TEMPLATES[
       "modal/not-dismissable"
@@ -67,16 +60,18 @@ acceptance("Modal", function (needs) {
 
     run(() => showModal("not-dismissable", {}));
 
-    assert.ok(queryAll(".d-modal:visible").length === 1, "modal should appear");
+    assert.equal(count(".d-modal:visible"), 1, "modal should appear");
 
     await click(".modal-outer-container");
-    assert.ok(
-      queryAll(".d-modal:visible").length === 1,
+    assert.equal(
+      count(".d-modal:visible"),
+      1,
       "modal should not disappear when you click outside"
     );
     await triggerKeyEvent("#main-outlet", "keyup", 27);
-    assert.ok(
-      queryAll(".d-modal:visible").length === 1,
+    assert.equal(
+      count(".d-modal:visible"),
+      1,
       "ESC should not close the modal"
     );
   });
@@ -126,7 +121,7 @@ acceptance("Modal", function (needs) {
 
     run(() => showModal("test-title"));
     assert.ok(
-      queryAll(".d-modal .title").length === 0,
+      !exists(".d-modal .title"),
       "it should not re-use the previous title"
     );
   });
@@ -142,20 +137,19 @@ acceptance("Modal Keyboard Events", function (needs) {
     await click(".admin-topic-timer-update button");
     await triggerKeyEvent(".d-modal", "keyup", 13);
 
-    assert.ok(
-      queryAll("#modal-alert:visible").length === 1,
+    assert.equal(
+      count("#modal-alert:visible"),
+      1,
       "hitting Enter triggers modal action"
     );
-    assert.ok(
-      queryAll(".d-modal:visible").length === 1,
+    assert.equal(
+      count(".d-modal:visible"),
+      1,
       "hitting Enter does not dismiss modal due to alert error"
     );
 
     await triggerKeyEvent("#main-outlet", "keyup", 27);
-    assert.ok(
-      queryAll(".d-modal:visible").length === 0,
-      "ESC should close the modal"
-    );
+    assert.ok(!exists(".d-modal:visible"), "ESC should close the modal");
 
     await click(".topic-body button.reply");
 
@@ -163,7 +157,7 @@ acceptance("Modal Keyboard Events", function (needs) {
 
     await triggerKeyEvent(".d-modal", "keyup", 13);
     assert.ok(
-      queryAll(".d-modal:visible").length === 0,
+      !exists(".d-modal:visible"),
       "modal should disappear on hitting Enter"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js b/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js
index 14ccee5afc4..25f46317389 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/notifications-filter-test.js
@@ -1,4 +1,4 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
 import { test } from "qunit";
 import { visit } from "@ember/test-helpers";
@@ -9,7 +9,7 @@ acceptance("Notifications filter", function (needs) {
   test("Notifications filter true", async function (assert) {
     await visit("/u/eviltrout/notifications");
 
-    assert.ok(queryAll(".large-notification").length >= 0);
+    assert.ok(exists(".large-notification"));
   });
 
   test("Notifications filter read", async function (assert) {
@@ -19,7 +19,7 @@ acceptance("Notifications filter", function (needs) {
     await dropdown.expand();
     await dropdown.selectRowByValue("read");
 
-    assert.ok(queryAll(".large-notification").length >= 0);
+    assert.ok(exists(".large-notification"));
   });
 
   test("Notifications filter unread", async function (assert) {
@@ -29,6 +29,6 @@ acceptance("Notifications filter", function (needs) {
     await dropdown.expand();
     await dropdown.selectRowByValue("unread");
 
-    assert.ok(queryAll(".large-notification").length >= 0);
+    assert.ok(exists(".large-notification"));
   });
 });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/notifications-test.js b/app/assets/javascripts/discourse/tests/acceptance/notifications-test.js
index 4ff691b9323..200b2900e0f 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/notifications-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/notifications-test.js
@@ -1,7 +1,10 @@
 import { visit } from "@ember/test-helpers";
 import {
   acceptance,
+  count,
   publishToMessageBus,
+  query,
+  queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
 
@@ -32,7 +35,7 @@ acceptance("User Notifications", function (needs) {
 
     await visit("/"); // wait for re-render
 
-    assert.equal(find("#quick-access-notifications li").length, 5);
+    assert.equal(count("#quick-access-notifications li"), 5);
 
     // high priority, unread notification - should be first
 
@@ -77,9 +80,9 @@ acceptance("User Notifications", function (needs) {
 
     await visit("/"); // wait for re-render
 
-    assert.equal(find("#quick-access-notifications li").length, 6);
+    assert.equal(count("#quick-access-notifications li"), 6);
     assert.equal(
-      find("#quick-access-notifications li span[data-topic-id]")[0].innerText,
+      query("#quick-access-notifications li span[data-topic-id]").innerText,
       "First notification"
     );
 
@@ -127,9 +130,10 @@ acceptance("User Notifications", function (needs) {
 
     await visit("/"); // wait for re-render
 
-    assert.equal(find("#quick-access-notifications li").length, 7);
+    assert.equal(count("#quick-access-notifications li"), 7);
     assert.equal(
-      find("#quick-access-notifications li span[data-topic-id]")[1].innerText,
+      queryAll("#quick-access-notifications li span[data-topic-id]")[1]
+        .innerText,
       "Second notification"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js
index c7028c7acb4..e69156dd531 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js
@@ -1,4 +1,9 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import { action } from "@ember/object";
 import { extraConnectorClass } from "discourse/lib/plugin-connectors";
@@ -65,12 +70,13 @@ acceptance("Plugin Outlet - Connector Class", function (needs) {
 
   test("Renders a template into the outlet", async function (assert) {
     await visit("/u/eviltrout");
-    assert.ok(
-      queryAll(".user-profile-primary-outlet.hello").length === 1,
+    assert.equal(
+      count(".user-profile-primary-outlet.hello"),
+      1,
       "it has class names"
     );
     assert.ok(
-      !queryAll(".user-profile-primary-outlet.dont-render").length,
+      !exists(".user-profile-primary-outlet.dont-render"),
       "doesn't render"
     );
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-multi-template-test.js b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-multi-template-test.js
index 640916bfeed..ae0e298abff 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-multi-template-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-multi-template-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { clearCache } from "discourse/lib/plugin-connectors";
 import hbs from "htmlbars-inline-precompile";
 import { test } from "qunit";
@@ -23,12 +27,14 @@ acceptance("Plugin Outlet - Multi Template", function (needs) {
 
   test("Renders a template into the outlet", async function (assert) {
     await visit("/u/eviltrout");
-    assert.ok(
-      queryAll(".user-profile-primary-outlet.hello").length === 1,
+    assert.equal(
+      count(".user-profile-primary-outlet.hello"),
+      1,
       "it has class names"
     );
-    assert.ok(
-      queryAll(".user-profile-primary-outlet.goodbye").length === 1,
+    assert.equal(
+      count(".user-profile-primary-outlet.goodbye"),
+      1,
       "it has class names"
     );
     assert.equal(
diff --git a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-single-template-test.js b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-single-template-test.js
index edd4504072f..27fc2e74439 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-single-template-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-single-template-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 import { test } from "qunit";
 import { visit } from "@ember/test-helpers";
@@ -19,8 +23,9 @@ acceptance("Plugin Outlet - Single Template", function (needs) {
 
   test("Renders a template into the outlet", async function (assert) {
     await visit("/u/eviltrout");
-    assert.ok(
-      queryAll(".user-profile-primary-outlet.hello").length === 1,
+    assert.equal(
+      count(".user-profile-primary-outlet.hello"),
+      1,
       "it has class names"
     );
     assert.equal(
diff --git a/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js b/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js
index a4ee8a6d23e..0014ebc2bfe 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
   updateCurrentUser,
@@ -495,27 +496,30 @@ acceptance("Security", function (needs) {
       I18n.t("user.auth_tokens.show_all", { count: 3 }),
       "it should display two tokens"
     );
-    assert.ok(
-      queryAll(".pref-auth-tokens .auth-token").length === 2,
+    assert.equal(
+      count(".pref-auth-tokens .auth-token"),
+      2,
       "it should display two tokens"
     );
 
     await click(".pref-auth-tokens > a:nth-of-type(1)");
 
-    assert.ok(
-      queryAll(".pref-auth-tokens .auth-token").length === 3,
+    assert.equal(
+      count(".pref-auth-tokens .auth-token"),
+      3,
       "it should display three tokens"
     );
 
     await click(".auth-token-dropdown button:nth-of-type(1)");
     await click("li[data-value='notYou']");
 
-    assert.ok(queryAll(".d-modal:visible").length === 1, "modal should appear");
+    assert.equal(count(".d-modal:visible"), 1, "modal should appear");
 
     await click(".modal-footer .btn-primary");
 
-    assert.ok(
-      queryAll(".pref-password.highlighted").length === 1,
+    assert.equal(
+      count(".pref-password.highlighted"),
+      1,
       "it should highlight password preferences"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js b/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js
index 24d8055fb7e..ba6465bf713 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  exists,
+  query,
+} from "discourse/tests/helpers/qunit-helpers";
 import {
   addRawTemplate,
   removeRawTemplate,
@@ -23,9 +27,9 @@ acceptance("Raw Plugin Outlet", function (needs) {
   });
   test("Renders the raw plugin outlet", async function (assert) {
     await visit("/");
-    assert.ok(queryAll(".topic-lala").length > 0, "it renders the outlet");
+    assert.ok(exists(".topic-lala"), "it renders the outlet");
     assert.equal(
-      queryAll(".topic-lala:nth-of-type(1)")[0].innerText,
+      query(".topic-lala:nth-of-type(1)").innerText,
       "11557",
       "it has the topic id"
     );
diff --git a/app/assets/javascripts/discourse/tests/acceptance/review-test.js b/app/assets/javascripts/discourse/tests/acceptance/review-test.js
index dbdf2f02898..522fe2c1891 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/review-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/review-test.js
@@ -1,6 +1,9 @@
 import {
   acceptance,
+  count,
+  exists,
   publishToMessageBus,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
@@ -16,18 +19,18 @@ acceptance("Review", function (needs) {
   test("It returns a list of reviewable items", async function (assert) {
     await visit("/review");
 
-    assert.ok(queryAll(".reviewable-item").length, "has a list of items");
-    assert.ok(queryAll(user).length);
+    assert.ok(exists(".reviewable-item"), "has a list of items");
+    assert.ok(exists(user));
     assert.ok(
-      queryAll(`${user}.reviewable-user`).length,
+      exists(`${user}.reviewable-user`),
       "applies a class for the type"
     );
     assert.ok(
-      queryAll(`${user} .reviewable-action.approve`).length,
+      exists(`${user} .reviewable-action.approve`),
       "creates a button for approve"
     );
     assert.ok(
-      queryAll(`${user} .reviewable-action.reject`).length,
+      exists(`${user} .reviewable-action.reject`),
       "creates a button for reject"
     );
   });
@@ -35,7 +38,7 @@ acceptance("Review", function (needs) {
   test("Grouped by topic", async function (assert) {
     await visit("/review/topics");
     assert.ok(
-      queryAll(".reviewable-topic").length,
+      exists(".reviewable-topic"),
       "it has a list of reviewable topics"
     );
   });
@@ -70,10 +73,7 @@ acceptance("Review", function (needs) {
   test("Settings", async function (assert) {
     await visit("/review/settings");
 
-    assert.ok(
-      queryAll(".reviewable-score-type").length,
-      "has a list of bonuses"
-    );
+    assert.ok(exists(".reviewable-score-type"), "has a list of bonuses");
 
     const field = selectKit(
       ".reviewable-score-type:nth-of-type(1) .field .combo-box"
@@ -82,15 +82,14 @@ acceptance("Review", function (needs) {
     await field.selectRowByValue("5");
     await click(".save-settings");
 
-    assert.ok(queryAll(".reviewable-settings .saved").length, "it saved");
+    assert.ok(exists(".reviewable-settings .saved"), "it saved");
   });
 
   test("Flag related", async function (assert) {
     await visit("/review");
 
     assert.ok(
-      queryAll(".reviewable-flagged-post .post-contents .username a[href]")
-        .length,
+      exists(".reviewable-flagged-post .post-contents .username a[href]"),
       "it has a link to the user"
     );
 
@@ -99,36 +98,26 @@ acceptance("Review", function (needs) {
       "<b>cooked content</b>"
     );
 
-    assert.equal(
-      queryAll(".reviewable-flagged-post .reviewable-score").length,
-      2
-    );
+    assert.equal(count(".reviewable-flagged-post .reviewable-score"), 2);
   });
 
   test("Flag related", async function (assert) {
     await visit("/review/1");
 
-    assert.ok(
-      queryAll(".reviewable-flagged-post").length,
-      "it shows the flagged post"
-    );
+    assert.ok(exists(".reviewable-flagged-post"), "it shows the flagged post");
   });
 
   test("Clicking the buttons triggers actions", async function (assert) {
     await visit("/review");
     await click(`${user} .reviewable-action.approve`);
-    assert.equal(
-      queryAll(user).length,
-      0,
-      "it removes the reviewable on success"
-    );
+    assert.ok(!exists(user), "it removes the reviewable on success");
   });
 
   test("Editing a reviewable", async function (assert) {
     const topic = '.reviewable-item[data-reviewable-id="4321"]';
     await visit("/review");
-    assert.ok(queryAll(`${topic} .reviewable-action.approve`).length);
-    assert.ok(!queryAll(`${topic} .category-name`).length);
+    assert.ok(exists(`${topic} .reviewable-action.approve`));
+    assert.ok(!exists(`${topic} .category-name`));
     assert.equal(
       queryAll(`${topic} .discourse-tag:nth-of-type(1)`).text(),
       "hello"
@@ -146,14 +135,13 @@ acceptance("Review", function (needs) {
     await click(`${topic} .reviewable-action.edit`);
     await click(`${topic} .reviewable-action.save-edit`);
     assert.ok(
-      queryAll(`${topic} .reviewable-action.approve`).length,
+      exists(`${topic} .reviewable-action.approve`),
       "saving without changes is a cancel"
     );
     await click(`${topic} .reviewable-action.edit`);
 
-    assert.equal(
-      queryAll(`${topic} .reviewable-action.approve`).length,
-      0,
+    assert.ok(
+      !exists(`${topic} .reviewable-action.approve`),
       "when editing actions are disabled"
     );
 
@@ -201,10 +189,10 @@ acceptance("Review", function (needs) {
   test("Reviewables can become stale", async function (assert) {
     await visit("/review");
 
-    const reviewable = find("[data-reviewable-id=1234]")[0];
+    const reviewable = query(`[data-reviewable-id="1234"]`);
     assert.notOk(reviewable.className.includes("reviewable-stale"));
-    assert.equal(find("[data-reviewable-id=1234] .status .pending").length, 1);
-    assert.equal(find(".stale-help").length, 0);
+    assert.equal(count(`[data-reviewable-id="1234"] .status .pending`), 1);
+    assert.ok(!exists(".stale-help"));
 
     publishToMessageBus("/reviewable_counts", {
       review_count: 1,
@@ -216,7 +204,7 @@ acceptance("Review", function (needs) {
     await visit("/review"); // wait for re-render
 
     assert.ok(reviewable.className.includes("reviewable-stale"));
-    assert.equal(find("[data-reviewable-id=1234] .status .approved").length, 1);
-    assert.equal(find(".stale-help").length, 1);
+    assert.equal(count("[data-reviewable-id=1234] .status .approved"), 1);
+    assert.equal(count(".stale-help"), 1);
   });
 });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/search-full-test.js b/app/assets/javascripts/discourse/tests/acceptance/search-full-test.js
index 48f0d1f9ed4..04e1fe1eba9 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/search-full-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/search-full-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
   selectDate,
@@ -96,19 +97,20 @@ acceptance("Search - Full Page", function (needs) {
 
     assert.ok($("body.search-page").length, "has body class");
     assert.ok(exists(".search-container"), "has container class");
-    assert.ok(queryAll(".search-query").length > 0);
-    assert.ok(queryAll(".fps-topic").length === 0);
+    assert.ok(exists(".search-query"));
+    assert.ok(!exists(".fps-topic"));
 
     await fillIn(".search-query", "none");
     await click(".search-cta");
 
-    assert.ok(queryAll(".fps-topic").length === 0, "has no results");
-    assert.ok(queryAll(".no-results-suggestion .google-search-form"));
+    assert.ok(!exists(".fps-topic"), "has no results");
+    assert.ok(exists(".no-results-suggestion"));
+    assert.ok(exists(".google-search-form"));
 
     await fillIn(".search-query", "discourse");
     await click(".search-cta");
 
-    assert.ok(queryAll(".fps-topic").length === 1, "has one post");
+    assert.equal(count(".fps-topic"), 1, "has one post");
   });
 
   test("search for personal messages", async function (assert) {
@@ -117,10 +119,11 @@ acceptance("Search - Full Page", function (needs) {
     await fillIn(".search-query", "discourse in:personal");
     await click(".search-cta");
 
-    assert.ok(queryAll(".fps-topic").length === 1, "has one post");
+    assert.equal(count(".fps-topic"), 1, "has one post");
 
-    assert.ok(
-      queryAll(".topic-status .personal_message").length === 1,
+    assert.equal(
+      count(".topic-status .personal_message"),
+      1,
       "shows the right icon"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/search-mobile-test.js b/app/assets/javascripts/discourse/tests/acceptance/search-mobile-test.js
index 0f302c2ca9c..c32647550eb 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/search-mobile-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/search-mobile-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -23,18 +24,19 @@ acceptance("Search - Mobile", function (needs) {
 
     await click(".search-advanced-title");
 
-    assert.ok(
-      queryAll(".search-advanced-filters").length === 1,
+    assert.equal(
+      count(".search-advanced-filters"),
+      1,
       "it should expand advanced search filters"
     );
 
     await fillIn(".search-query", "discourse");
     await click(".search-cta");
 
-    assert.ok(queryAll(".fps-topic").length === 1, "has one post");
+    assert.equal(count(".fps-topic"), 1, "has one post");
 
     assert.ok(
-      queryAll(".search-advanced-filters").length === 0,
+      !exists(".search-advanced-filters"),
       "it should collapse advanced search filters"
     );
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/share-topic-test.js b/app/assets/javascripts/discourse/tests/acceptance/share-topic-test.js
index ed50a785c45..0d8e4c00490 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/share-topic-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/share-topic-test.js
@@ -1,6 +1,7 @@
 import { click, visit } from "@ember/test-helpers";
 import {
   acceptance,
+  count,
   exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -29,7 +30,7 @@ acceptance("Share and Invite modal", function (needs) {
       "it shows the topic sharing url"
     );
 
-    assert.ok(queryAll(".social-link").length > 1, "it shows social sources");
+    assert.ok(count(".social-link") > 1, "it shows social sources");
 
     assert.ok(
       exists(".btn-primary[aria-label='Notify']"),
diff --git a/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js b/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js
index 493d361eb50..3b498dd35d3 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  exists,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
 import { test } from "qunit";
@@ -6,19 +10,19 @@ import { test } from "qunit";
 acceptance("Shared Drafts", function () {
   test("Viewing and publishing", async function (assert) {
     await visit("/t/some-topic/9");
-    assert.ok(queryAll(".shared-draft-controls").length === 1);
+    assert.equal(count(".shared-draft-controls"), 1);
     let categoryChooser = selectKit(".shared-draft-controls .category-chooser");
     assert.equal(categoryChooser.header().value(), "3");
 
     await click(".publish-shared-draft");
     await click(".bootbox .btn-primary");
 
-    assert.ok(queryAll(".shared-draft-controls").length === 0);
+    assert.ok(!exists(".shared-draft-controls"));
   });
 
   test("Updating category", async function (assert) {
     await visit("/t/some-topic/9");
-    assert.ok(queryAll(".shared-draft-controls").length === 1);
+    assert.equal(count(".shared-draft-controls"), 1);
 
     await click(".edit-topic");
 
diff --git a/app/assets/javascripts/discourse/tests/acceptance/sign-in-test.js b/app/assets/javascripts/discourse/tests/acceptance/sign-in-test.js
index 9b6136663aa..55c46a3e0b7 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/sign-in-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/sign-in-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -64,12 +65,12 @@ acceptance("Signing In", function () {
     await click(".modal-footer button.edit-email");
     assert.equal(queryAll(".activate-new-email").val(), "current@example.com");
     assert.equal(
-      queryAll(".modal-footer .btn-primary:disabled").length,
+      count(".modal-footer .btn-primary:disabled"),
       1,
       "must change email"
     );
     await fillIn(".activate-new-email", "different@example.com");
-    assert.equal(queryAll(".modal-footer .btn-primary:disabled").length, 0);
+    assert.ok(!exists(".modal-footer .btn-primary:disabled"));
     await click(".modal-footer .btn-primary");
     assert.equal(queryAll(".modal-body b").text(), "different@example.com");
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/tag-groups-test.js b/app/assets/javascripts/discourse/tests/acceptance/tag-groups-test.js
index 352f6746e06..3de7f851df1 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/tag-groups-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/tag-groups-test.js
@@ -1,7 +1,7 @@
 import {
   acceptance,
   exists,
-  queryAll,
+  query,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -53,7 +53,7 @@ acceptance("Tag Groups", function (needs) {
     await tags.expand();
 
     await click(".group-tags-list .tag-chooser .choice:nth-of-type(1)");
-    assert.ok(!queryAll(".tag-group-content .btn.btn-danger")[0].disabled);
+    assert.ok(!query(".tag-group-content .btn.btn-danger").disabled);
   });
 
   test("tag groups can have multiple groups added to them", async function (assert) {
@@ -72,7 +72,7 @@ acceptance("Tag Groups", function (needs) {
     await groups.selectRowByIndex(1);
     await groups.selectRowByIndex(0);
 
-    assert.ok(!queryAll(".tag-group-content .btn.btn-primary")[0].disabled);
+    assert.ok(!query(".tag-group-content .btn.btn-primary").disabled);
 
     await click(".tag-group-content .btn.btn-primary");
     await click(".tag-groups-sidebar li:first-child a");
diff --git a/app/assets/javascripts/discourse/tests/acceptance/tags-test.js b/app/assets/javascripts/discourse/tests/acceptance/tags-test.js
index 7e45eda7184..3c259a88bb4 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/tags-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/tags-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   invisible,
   queryAll,
@@ -233,18 +234,18 @@ acceptance("Tags listed by group", function (needs) {
     updateCurrentUser({ moderator: false, admin: false });
 
     await visit("/tag/regular-tag");
-    assert.ok(queryAll("#create-topic:disabled").length === 0);
+    assert.ok(!exists("#create-topic:disabled"));
 
     await visit("/tag/staff-only-tag");
-    assert.ok(queryAll("#create-topic:disabled").length === 1);
+    assert.equal(count("#create-topic:disabled"), 1);
 
     updateCurrentUser({ moderator: true });
 
     await visit("/tag/regular-tag");
-    assert.ok(queryAll("#create-topic:disabled").length === 0);
+    assert.ok(!exists("#create-topic:disabled"));
 
     await visit("/tag/staff-only-tag");
-    assert.ok(queryAll("#create-topic:disabled").length === 0);
+    assert.ok(!exists("#create-topic:disabled"));
   });
 });
 
@@ -383,7 +384,7 @@ acceptance("Tag info", function (needs) {
     updateCurrentUser({ moderator: false, admin: false });
 
     await visit("/tag/planters");
-    assert.ok(queryAll("#show-tag-info").length === 1);
+    assert.equal(count("#show-tag-info"), 1);
 
     await click("#show-tag-info");
     assert.ok(exists(".tag-info .tag-name"), "show tag");
@@ -391,14 +392,12 @@ acceptance("Tag info", function (needs) {
       queryAll(".tag-info .tag-associations").text().indexOf("Gardening") >= 0,
       "show tag group names"
     );
-    assert.ok(
-      queryAll(".tag-info .synonyms-list .tag-box").length === 2,
+    assert.equal(
+      count(".tag-info .synonyms-list .tag-box"),
+      2,
       "shows the synonyms"
     );
-    assert.ok(
-      queryAll(".tag-info .badge-category").length === 1,
-      "show the category"
-    );
+    assert.equal(count(".tag-info .badge-category"), 1, "show the category");
     assert.ok(!exists("#rename-tag"), "can't rename tag");
     assert.ok(!exists("#edit-synonyms"), "can't edit synonyms");
     assert.ok(!exists("#delete-tag"), "can't delete tag");
@@ -408,7 +407,7 @@ acceptance("Tag info", function (needs) {
     updateCurrentUser({ moderator: false, admin: true });
 
     await visit("/tag/happy-monkey");
-    assert.ok(queryAll("#show-tag-info").length === 1);
+    assert.equal(count("#show-tag-info"), 1);
 
     await click("#show-tag-info");
     assert.ok(exists(".tag-info .tag-name"), "show tag");
@@ -416,7 +415,7 @@ acceptance("Tag info", function (needs) {
     await click("#edit-synonyms");
     await click("#add-synonyms .filter-input");
 
-    assert.equal(find(".tag-chooser-row").length, 2);
+    assert.equal(count(".tag-chooser-row"), 2);
     assert.deepEqual(
       Array.from(find(".tag-chooser-row")).map((x) => x.dataset["value"]),
       ["monkey", "not-monkey"]
@@ -436,7 +435,7 @@ acceptance("Tag info", function (needs) {
     updateCurrentUser({ moderator: false, admin: true });
 
     await visit("/tag/planters");
-    assert.ok(queryAll("#show-tag-info").length === 1);
+    assert.equal(count("#show-tag-info"), 1);
 
     await click("#show-tag-info");
     assert.ok(exists("#rename-tag"), "can rename tag");
@@ -444,18 +443,13 @@ acceptance("Tag info", function (needs) {
     assert.ok(exists("#delete-tag"), "can delete tag");
 
     await click("#edit-synonyms");
-    assert.ok(
-      queryAll(".unlink-synonym:visible").length === 2,
-      "unlink UI is visible"
-    );
-    assert.ok(
-      queryAll(".delete-synonym:visible").length === 2,
-      "delete UI is visible"
-    );
+    assert.ok(count(".unlink-synonym:visible"), 2, "unlink UI is visible");
+    assert.equal(count(".delete-synonym:visible"), 2, "delete UI is visible");
 
     await click(".unlink-synonym:nth-of-type(1)");
-    assert.ok(
-      queryAll(".tag-info .synonyms-list .tag-box").length === 1,
+    assert.equal(
+      count(".tag-info .synonyms-list .tag-box"),
+      1,
       "removed a synonym"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-anonymous-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-anonymous-test.js
index d07cac23812..e0c1b80dde5 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/topic-anonymous-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/topic-anonymous-test.js
@@ -1,8 +1,4 @@
-import {
-  acceptance,
-  exists,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
 import { test } from "qunit";
 import { visit } from "@ember/test-helpers";
 
@@ -12,7 +8,7 @@ acceptance("Topic - Anonymous", function () {
     assert.ok(exists("#topic"), "The topic was rendered");
     assert.ok(exists("#topic .cooked"), "The topic has cooked posts");
     assert.ok(
-      queryAll(".shared-draft-notice").length === 0,
+      !exists(".shared-draft-notice"),
       "no shared draft unless there's a dest category id"
     );
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js
index a72cfecdfdf..66d78a2ea08 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/topic-quote-button-test.js
@@ -29,11 +29,7 @@ acceptance("Topic - Quote button - logged in", function (needs) {
     await visit("/t/internationalization-localization/280");
     await selectText("#post_5 blockquote");
     assert.ok(exists(".insert-quote"), "it shows the quote button");
-    assert.equal(
-      queryAll(".quote-sharing").length,
-      0,
-      "it does not show quote sharing"
-    );
+    assert.ok(!exists(".quote-sharing"), "it does not show quote sharing");
   });
 
   test("Shows quote share buttons with the right site settings", async function (assert) {
@@ -85,11 +81,7 @@ acceptance("Topic - Quote button - anonymous", function (needs) {
       exists(`.quote-sharing .btn[title='${I18n.t("share.email")}']`),
       "it includes the email share button"
     );
-    assert.equal(
-      queryAll(".insert-quote").length,
-      0,
-      "it does not show the quote button"
-    );
+    assert.ok(!exists(".insert-quote"), "it does not show the quote button");
   });
 
   test("Shows single share button when site setting only has one item", async function (assert) {
@@ -103,9 +95,8 @@ acceptance("Topic - Quote button - anonymous", function (needs) {
       exists(`.quote-sharing .btn[title='${I18n.t("share.twitter")}']`),
       "it includes the twitter share button"
     );
-    assert.equal(
-      queryAll(".quote-share-label").length,
-      0,
+    assert.ok(
+      !exists(".quote-share-label"),
       "it does not show the Share label"
     );
   });
@@ -116,16 +107,7 @@ acceptance("Topic - Quote button - anonymous", function (needs) {
     await visit("/t/internationalization-localization/280");
     await selectText("#post_5 blockquote");
 
-    assert.equal(
-      queryAll(".quote-sharing").length,
-      0,
-      "it does not show quote sharing"
-    );
-
-    assert.equal(
-      queryAll(".insert-quote").length,
-      0,
-      "it does not show the quote button"
-    );
+    assert.ok(!exists(".quote-sharing"), "it does not show quote sharing");
+    assert.ok(!exists(".insert-quote"), "it does not show the quote button");
   });
 });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-test.js
index 32fd762985b..52338fc035e 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/topic-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/topic-test.js
@@ -1,6 +1,8 @@
 import {
   acceptance,
+  count,
   exists,
+  query,
   queryAll,
   visible,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -140,16 +142,13 @@ acceptance("Topic", function (needs) {
   test("Marking a topic as wiki", async function (assert) {
     await visit("/t/internationalization-localization/280");
 
-    assert.ok(
-      queryAll("a.wiki").length === 0,
-      "it does not show the wiki icon"
-    );
+    assert.ok(!exists("a.wiki"), "it does not show the wiki icon");
 
     await click(".topic-post:nth-of-type(1) button.show-more-actions");
     await click(".topic-post:nth-of-type(1) button.show-post-admin-menu");
     await click(".btn.wiki");
 
-    assert.ok(queryAll("button.wiki").length === 1, "it shows the wiki icon");
+    assert.equal(count("button.wiki"), 1, "it shows the wiki icon");
   });
 
   test("Visit topic routes", async function (assert) {
@@ -337,7 +336,7 @@ acceptance("Topic", function (needs) {
     await visit("/t/internationalization-localization/280");
     await click(".gap");
 
-    assert.equal(queryAll(".gap").length, 0, "it hides gap");
+    assert.ok(!exists(".gap"), "it hides gap");
   });
 
   test("Quoting a quote keeps the original poster name", async function (assert) {
@@ -448,12 +447,12 @@ acceptance("Topic with title decorated", function (needs) {
     await visit("/t/internationalization-localization/280");
 
     assert.ok(
-      queryAll(".fancy-title")[0].innerText.endsWith("-280-topic-title"),
+      query(".fancy-title").innerText.endsWith("-280-topic-title"),
       "it decorates topic title"
     );
 
     assert.ok(
-      queryAll(".raw-topic-link:nth-child(1)")[0].innerText.endsWith(
+      query(".raw-topic-link:nth-child(1)").innerText.endsWith(
         "-27331-topic-list-item-title"
       ),
       "it decorates topic list item title"
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-anonymous-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-anonymous-test.js
index 5e86c5c869f..6274bf797ee 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-anonymous-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-anonymous-test.js
@@ -1,8 +1,4 @@
-import {
-  acceptance,
-  count,
-  exists,
-} from "discourse/tests/helpers/qunit-helpers";
+import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
 import { currentRouteName, currentURL, visit } from "@ember/test-helpers";
 import { test } from "qunit";
 
@@ -17,15 +13,15 @@ acceptance("User Anonymous", function () {
     await visit("/u/eviltrout/activity");
     assert.ok($("body.user-activity-page").length, "has the body class");
     assert.ok(exists(".user-main .about"), "it has the about section");
-    assert.ok(count(".user-stream .item") > 0, "it has stream items");
+    assert.ok(exists(".user-stream .item"), "it has stream items");
 
     await visit("/u/eviltrout/activity/topics");
-    assert.equal(count(".user-stream .item"), 0, "has no stream displayed");
-    assert.ok(count(".topic-list tr") > 0, "it has a topic list");
+    assert.ok(!exists(".user-stream .item"), "has no stream displayed");
+    assert.ok(exists(".topic-list tr"), "it has a topic list");
 
     await visit("/u/eviltrout/activity/replies");
     assert.ok(exists(".user-main .about"), "it has the about section");
-    assert.ok(count(".user-stream .item") > 0, "it has stream items");
+    assert.ok(exists(".user-stream .item"), "it has stream items");
 
     assert.ok(exists(".user-stream.filter-5"), "stream has filter class");
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
index 79c6dd90e1f..366465352da 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
@@ -14,7 +14,7 @@ acceptance("User's bookmarks", function (needs) {
 
   test("removing a bookmark with no reminder does not show a confirmation", async function (assert) {
     await visit("/u/eviltrout/activity/bookmarks");
-    assert.ok(queryAll(".bookmark-list-item").length > 0);
+    assert.ok(exists(".bookmark-list-item"));
 
     const dropdown = selectKit(".bookmark-actions-dropdown:nth-of-type(1)");
     await dropdown.expand();
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js
index 2f39a4cfdec..1b2aa090f5b 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js
@@ -1,5 +1,7 @@
 import {
   acceptance,
+  count,
+  exists,
   queryAll,
   visible,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -11,21 +13,22 @@ acceptance("User Drafts", function (needs) {
 
   test("Stream", async function (assert) {
     await visit("/u/eviltrout/activity/drafts");
-    assert.ok(queryAll(".user-stream-item").length === 3, "has drafts");
+    assert.equal(count(".user-stream-item"), 3, "has drafts");
 
     await click(".user-stream-item:last-child .remove-draft");
     assert.ok(visible(".bootbox"));
 
     await click(".bootbox .btn-primary");
-    assert.ok(
-      queryAll(".user-stream-item").length === 2,
+    assert.equal(
+      count(".user-stream-item"),
+      2,
       "draft removed, list length diminished by one"
     );
   });
 
   test("Stream - resume draft", async function (assert) {
     await visit("/u/eviltrout/activity/drafts");
-    assert.ok(queryAll(".user-stream-item").length > 0, "has drafts");
+    assert.ok(exists(".user-stream-item"), "has drafts");
 
     await click(".user-stream-item .resume-draft");
     assert.equal(
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-interface-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-interface-test.js
index d2ad0e4ef65..6869f9fbad9 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-interface-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-interface-test.js
@@ -1,5 +1,6 @@
 import {
   acceptance,
+  count,
   exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -125,10 +126,7 @@ acceptance("User Preferences - Interface", function (needs) {
     assert.equal(selectKit(".theme .select-kit").header().value(), 2);
 
     await selectKit(".light-color-scheme .select-kit").expand();
-    assert.equal(
-      queryAll(".light-color-scheme .select-kit .select-kit-row").length,
-      2
-    );
+    assert.equal(count(".light-color-scheme .select-kit .select-kit-row"), 2);
 
     document.querySelector("meta[name='discourse_theme_ids']").remove();
   });
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-notifications-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-notifications-test.js
index 10dd6b8e8ce..285367d713c 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-notifications-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-notifications-test.js
@@ -1,7 +1,7 @@
 import {
   acceptance,
+  count,
   exists,
-  queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click, visit } from "@ember/test-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -78,7 +78,7 @@ acceptance("User notification schedule", function (needs) {
       "set monday label to none"
     );
     assert.equal(
-      queryAll(".day.Monday .select-kit.single-select").length,
+      count(".day.Monday .select-kit.single-select"),
       1,
       "The end time input is hidden"
     );
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 30105cf388f..034c228cfaf 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
@@ -3,6 +3,7 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
@@ -15,7 +16,7 @@ discourseModule("Integration | Component | ace-editor", function (hooks) {
     template: hbs`{{ace-editor mode="css"}}`,
     test(assert) {
       assert.expect(1);
-      assert.ok(queryAll(".ace_editor").length, "it renders the ace editor");
+      assert.ok(exists(".ace_editor"), "it renders the ace editor");
     },
   });
 
@@ -24,7 +25,7 @@ discourseModule("Integration | Component | ace-editor", function (hooks) {
     template: hbs`{{ace-editor mode="html" content="<b>wat</b>"}}`,
     test(assert) {
       assert.expect(1);
-      assert.ok(queryAll(".ace_editor").length, "it renders the ace editor");
+      assert.ok(exists(".ace_editor"), "it renders the ace editor");
     },
   });
 
@@ -33,7 +34,7 @@ discourseModule("Integration | Component | ace-editor", function (hooks) {
     template: hbs`{{ace-editor mode="sql" content="SELECT * FROM users"}}`,
     test(assert) {
       assert.expect(1);
-      assert.ok(queryAll(".ace_editor").length, "it renders the ace editor");
+      assert.ok(exists(".ace_editor"), "it renders the ace editor");
     },
   });
 
diff --git a/app/assets/javascripts/discourse/tests/integration/components/activation-controls-test.js b/app/assets/javascripts/discourse/tests/integration/components/activation-controls-test.js
index f8a1668a102..1c8c5ae730e 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/activation-controls-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/activation-controls-test.js
@@ -1,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
 discourseModule(
@@ -20,7 +17,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.equal(queryAll("button.edit-email").length, 0);
+        assert.ok(!exists("button.edit-email"));
       },
     });
   }
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 284f5dcd5c0..df9a581b73b 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
@@ -2,6 +2,7 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
   exists,
   queryAll,
@@ -94,7 +95,7 @@ discourseModule("Integration | Component | admin-report", function (hooks) {
     test(assert) {
       assert.ok(exists(".pagination"), "it paginates the results");
       assert.equal(
-        queryAll(".pagination button").length,
+        count(".pagination button"),
         3,
         "it creates the correct number of pages"
       );
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 05e87b82361..101b02ce3af 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,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, query } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 import pretender from "discourse/tests/helpers/create-pretender";
 import { resetCache } from "pretty-text/upload-short-url";
@@ -16,7 +13,7 @@ discourseModule("Integration | Component | cook-text", function (hooks) {
     template: hbs`{{cook-text "_foo_" class="post-body"}}`,
 
     test(assert) {
-      const html = queryAll(".post-body")[0].innerHTML.trim();
+      const html = query(".post-body").innerHTML.trim();
       assert.equal(html, "<p><em>foo</em></p>");
     },
   });
@@ -45,7 +42,7 @@ discourseModule("Integration | Component | cook-text", function (hooks) {
     },
 
     test(assert) {
-      const html = queryAll(".post-body")[0].innerHTML.trim();
+      const html = query(".post-body").innerHTML.trim();
       assert.equal(
         html,
         '<p><img src="/images/avatar.png" alt="an image"></p>'
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 8c6eed65a37..b0340a40187 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
@@ -18,13 +18,10 @@ discourseModule("Integration | Component | d-button", function (hooks) {
 
     test(assert) {
       assert.ok(
-        queryAll("button.btn.btn-icon.no-text").length,
+        exists("button.btn.btn-icon.no-text"),
         "it has all the classes"
       );
-      assert.ok(
-        queryAll("button .d-icon.d-icon-plus").length,
-        "it has the icon"
-      );
+      assert.ok(exists("button .d-icon.d-icon-plus"), "it has the icon");
       assert.equal(
         queryAll("button").attr("tabindex"),
         "3",
@@ -37,18 +34,9 @@ discourseModule("Integration | Component | d-button", function (hooks) {
     template: hbs`{{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"
-      );
+      assert.ok(exists("button.btn.btn-icon-text"), "it has all the classes");
+      assert.ok(exists("button .d-icon.d-icon-plus"), "it has the icon");
+      assert.ok(exists("button span.d-button-label"), "it has the label");
     },
   });
 
@@ -56,14 +44,8 @@ discourseModule("Integration | Component | d-button", function (hooks) {
     template: hbs`{{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"
-      );
+      assert.ok(exists("button.btn.btn-text"), "it has all the classes");
+      assert.ok(exists("button span.d-button-label"), "it has the label");
     },
   });
 
@@ -80,7 +62,7 @@ discourseModule("Integration | Component | d-button", function (hooks) {
 
     test(assert) {
       assert.ok(
-        queryAll("button.btn-link:not(.btn)").length,
+        exists("button.btn-link:not(.btn)"),
         "it has the right classes"
       );
     },
@@ -95,22 +77,22 @@ discourseModule("Integration | Component | d-button", function (hooks) {
 
     test(assert) {
       assert.ok(
-        queryAll("button.is-loading .loading-icon").length,
+        exists("button.is-loading .loading-icon"),
         "it has a spinner showing"
       );
       assert.ok(
-        queryAll("button[disabled]").length,
+        exists("button[disabled]"),
         "while loading the button is disabled"
       );
 
       this.set("isLoading", false);
 
       assert.notOk(
-        queryAll("button .loading-icon").length,
+        exists("button .loading-icon"),
         "it doesn't have a spinner showing"
       );
       assert.ok(
-        queryAll("button:not([disabled])").length,
+        exists("button:not([disabled])"),
         "while not loading the button is enabled"
       );
     },
@@ -124,14 +106,11 @@ discourseModule("Integration | Component | d-button", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll("button[disabled]").length, "the button is disabled");
+      assert.ok(exists("button[disabled]"), "the button is disabled");
 
       this.set("disabled", false);
 
-      assert.ok(
-        queryAll("button:not([disabled])").length,
-        "the button is enabled"
-      );
+      assert.ok(exists("button:not([disabled])"), "the button is enabled");
     },
   });
 
@@ -146,7 +125,7 @@ discourseModule("Integration | Component | d-button", function (hooks) {
       this.set("ariaLabel", "test.fooAriaLabel");
 
       assert.equal(
-        queryAll("button")[0].getAttribute("aria-label"),
+        query("button").getAttribute("aria-label"),
         I18n.t("test.fooAriaLabel")
       );
 
@@ -155,7 +134,7 @@ discourseModule("Integration | Component | d-button", function (hooks) {
         translatedAriaLabel: "bar",
       });
 
-      assert.equal(queryAll("button")[0].getAttribute("aria-label"), "bar");
+      assert.equal(query("button").getAttribute("aria-label"), "bar");
     },
   });
 
@@ -169,7 +148,7 @@ discourseModule("Integration | Component | d-button", function (hooks) {
     test(assert) {
       this.set("title", "test.fooTitle");
       assert.equal(
-        queryAll("button")[0].getAttribute("title"),
+        query("button").getAttribute("title"),
         I18n.t("test.fooTitle")
       );
 
@@ -178,7 +157,7 @@ discourseModule("Integration | Component | d-button", function (hooks) {
         translatedTitle: "bar",
       });
 
-      assert.equal(queryAll("button")[0].getAttribute("title"), "bar");
+      assert.equal(query("button").getAttribute("title"), "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 f088f40f683..047ce443a09 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
@@ -4,6 +4,8 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import {
@@ -24,7 +26,7 @@ discourseModule("Integration | Component | d-editor", function (hooks) {
     template: hbs`{{d-editor value=value}}`,
 
     async test(assert) {
-      assert.ok(queryAll(".d-editor-button-bar").length);
+      assert.ok(exists(".d-editor-button-bar"));
       await fillIn(".d-editor-input", "hello **world**");
 
       assert.equal(this.value, "hello **world**");
@@ -80,7 +82,7 @@ discourseModule("Integration | Component | d-editor", function (hooks) {
         this.set("value", "hello world.");
       },
       test(assert) {
-        const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]);
+        const textarea = jumpEnd(query("textarea.d-editor-input"));
         testFunc.call(this, assert, textarea);
       },
     });
@@ -94,7 +96,7 @@ discourseModule("Integration | Component | d-editor", function (hooks) {
       },
 
       test(assert) {
-        const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]);
+        const textarea = jumpEnd(query("textarea.d-editor-input"));
         testFunc.call(this, assert, textarea);
       },
     });
@@ -236,7 +238,7 @@ discourseModule("Integration | Component | d-editor", function (hooks) {
     },
 
     async test(assert) {
-      const textarea = queryAll("textarea.d-editor-input")[0];
+      const textarea = query("textarea.d-editor-input");
       textarea.selectionStart = 0;
       textarea.selectionEnd = textarea.value.length;
 
@@ -261,7 +263,7 @@ discourseModule("Integration | Component | d-editor", function (hooks) {
     },
 
     async test(assert) {
-      const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]);
+      const textarea = jumpEnd(query("textarea.d-editor-input"));
 
       await click("button.code");
       assert.equal(this.value, `    ${I18n.t("composer.code_text")}`);
@@ -346,7 +348,7 @@ third line`
     },
 
     async test(assert) {
-      const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]);
+      const textarea = jumpEnd(query("textarea.d-editor-input"));
 
       await click("button.code");
       assert.equal(
@@ -458,7 +460,7 @@ third line`
       this.set("value", "one\n\ntwo\n\nthree");
     },
     async test(assert) {
-      const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]);
+      const textarea = jumpEnd(query("textarea.d-editor-input"));
 
       textarea.selectionStart = 0;
 
@@ -479,7 +481,7 @@ third line`
       this.set("value", "one\n\n\n\ntwo");
     },
     async test(assert) {
-      const textarea = jumpEnd(queryAll("textarea.d-editor-input")[0]);
+      const textarea = jumpEnd(query("textarea.d-editor-input"));
 
       textarea.selectionStart = 6;
       textarea.selectionEnd = 10;
@@ -676,7 +678,7 @@ third line`
     },
 
     async test(assert) {
-      jumpEnd(queryAll("textarea.d-editor-input")[0]);
+      jumpEnd(query("textarea.d-editor-input"));
       await click("button.emoji");
 
       await click(
@@ -721,7 +723,7 @@ third line`
     },
 
     async test(assert) {
-      let element = queryAll(".d-editor")[0];
+      let element = query(".d-editor");
       await paste(element, "\ta\tb\n1\t2\t3");
       assert.equal(this.value, "||a|b|\n|---|---|---|\n|1|2|3|\n");
     },
@@ -735,7 +737,7 @@ third line`
     },
 
     async test(assert) {
-      let element = queryAll(".d-editor")[0];
+      let element = query(".d-editor");
       await paste(element, '\ta\tb\n1\t"2\n2.5"\t3');
       assert.equal(this.value, "||a|b|\n|---|---|---|\n|1|2<br>2.5|3|\n");
     },
diff --git a/app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js b/app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js
index 69f9247521c..4f46c7ac68e 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/group-membership-button-test.js
@@ -1,6 +1,7 @@
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
 } from "discourse/tests/helpers/qunit-helpers";
 import componentTest, {
   setupRenderingTest,
@@ -21,21 +22,18 @@ discourseModule(
 
       async test(assert) {
         assert.ok(
-          queryAll(".group-index-join").length === 0,
+          !exists(".group-index-join"),
           "can't join group if public_admission is false"
         );
 
         this.set("model.public_admission", true);
         assert.ok(
-          queryAll(".group-index-join").length === 0,
+          !exists(".group-index-join"),
           "can't join group if user is already in the group"
         );
 
         this.set("model.is_group_user", false);
-        assert.ok(
-          queryAll(".group-index-join").length,
-          "allowed to join group"
-        );
+        assert.ok(exists(".group-index-join"), "allowed to join group");
       },
     });
 
@@ -46,21 +44,18 @@ discourseModule(
       },
       async test(assert) {
         assert.ok(
-          queryAll(".group-index-leave").length === 0,
+          !exists(".group-index-leave"),
           "can't leave group if public_exit is false"
         );
 
         this.set("model.public_exit", true);
         assert.ok(
-          queryAll(".group-index-leave").length === 0,
+          !exists(".group-index-leave"),
           "can't leave group if user is not in the group"
         );
 
         this.set("model.is_group_user", true);
-        assert.ok(
-          queryAll(".group-index-leave").length === 1,
-          "allowed to leave group"
-        );
+        assert.equal(count(".group-index-leave"), 1, "allowed to leave group");
       },
     });
 
@@ -75,12 +70,12 @@ discourseModule(
 
       async test(assert) {
         assert.ok(
-          queryAll(".group-index-request").length === 0,
+          !exists(".group-index-request"),
           "can't request for membership if user is already in the group"
         );
         this.set("model.is_group_user", false);
         assert.ok(
-          queryAll(".group-index-request").length,
+          exists(".group-index-request"),
           "allowed to request for group membership"
         );
       },
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 61af90d0fd8..02c06a50540 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
@@ -2,8 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click } from "@ember/test-helpers";
 import hbs from "htmlbars-inline-precompile";
@@ -18,20 +19,19 @@ discourseModule("Integration | Component | image-uploader", function (hooks) {
 
     async test(assert) {
       assert.equal(
-        queryAll(".d-icon-far-image").length,
+        count(".d-icon-far-image"),
         1,
         "it displays the upload icon"
       );
 
       assert.equal(
-        queryAll(".d-icon-far-trash-alt").length,
+        count(".d-icon-far-trash-alt"),
         1,
         "it displays the trash icon"
       );
 
-      assert.equal(
-        queryAll(".placeholder-overlay").length,
-        0,
+      assert.ok(
+        !exists(".placeholder-overlay"),
         "it does not display the placeholder image"
       );
 
@@ -50,20 +50,18 @@ discourseModule("Integration | Component | image-uploader", function (hooks) {
 
     test(assert) {
       assert.equal(
-        queryAll(".d-icon-far-image").length,
+        count(".d-icon-far-image"),
         1,
         "it displays the upload icon"
       );
 
-      assert.equal(
-        queryAll(".d-icon-far-trash-alt").length,
-        0,
+      assert.ok(
+        !exists(".d-icon-far-trash-alt"),
         "it does not display trash icon"
       );
 
-      assert.equal(
-        queryAll(".image-uploader-lightbox-btn").length,
-        0,
+      assert.ok(
+        !exists(".image-uploader-lightbox-btn"),
         "it does not display the button to open image lightbox"
       );
     },
@@ -74,25 +72,23 @@ discourseModule("Integration | Component | image-uploader", function (hooks) {
 
     test(assert) {
       assert.equal(
-        queryAll(".d-icon-far-image").length,
+        count(".d-icon-far-image"),
         1,
         "it displays the upload icon"
       );
 
-      assert.equal(
-        queryAll(".d-icon-far-trash-alt").length,
-        0,
+      assert.ok(
+        !exists(".d-icon-far-trash-alt"),
         "it does not display trash icon"
       );
 
-      assert.equal(
-        queryAll(".image-uploader-lightbox-btn").length,
-        0,
+      assert.ok(
+        !exists(".image-uploader-lightbox-btn"),
         "it does not display the button to open image lightbox"
       );
 
       assert.equal(
-        queryAll(".placeholder-overlay").length,
+        count(".placeholder-overlay"),
         1,
         "it displays the placeholder image"
       );
diff --git a/app/assets/javascripts/discourse/tests/integration/components/invite-panel-test.js b/app/assets/javascripts/discourse/tests/integration/components/invite-panel-test.js
index 8d161d20226..965b4800236 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/invite-panel-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/invite-panel-test.js
@@ -5,10 +5,7 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import pretender from "discourse/tests/helpers/create-pretender";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
 import hbs from "htmlbars-inline-precompile";
 
@@ -45,7 +42,7 @@ discourseModule("Integration | Component | invite-panel", function (hooks) {
       await input.expand();
       await fillIn(".invite-user-input .filter-input", "eviltrout@example.com");
       await input.selectRowByValue("eviltrout@example.com");
-      assert.ok(queryAll(".send-invite:disabled").length === 0);
+      assert.ok(!exists(".send-invite:disabled"));
       await click(".generate-invite-link");
       assert.equal(
         find(".invite-link-input")[0].value,
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 6e5fb866e30..7e9651a86c5 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
@@ -3,7 +3,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import I18n from "I18n";
@@ -23,8 +25,9 @@ discourseModule(
         await fillIn(".new-value-input.key", "thirdKey");
         await click(".add-value-btn");
 
-        assert.ok(
-          queryAll(".values .value").length === 2,
+        assert.equal(
+          count(".values .value"),
+          2,
           "it doesn't add the value to the list if secret is missing"
         );
 
@@ -32,8 +35,9 @@ discourseModule(
         await fillIn(".new-value-input.secret", "thirdValue");
         await click(".add-value-btn");
 
-        assert.ok(
-          queryAll(".values .value").length === 2,
+        assert.equal(
+          count(".values .value"),
+          2,
           "it doesn't add the value to the list if key is missing"
         );
 
@@ -41,8 +45,9 @@ discourseModule(
         await fillIn(".new-value-input.secret", "thirdValue");
         await click(".add-value-btn");
 
-        assert.ok(
-          queryAll(".values .value").length === 3,
+        assert.equal(
+          count(".values .value"),
+          3,
           "it adds the value to the list of values"
         );
 
@@ -63,7 +68,7 @@ discourseModule(
         await click(".add-value-btn");
 
         assert.ok(
-          queryAll(".values .value").length === 0,
+          !exists(".values .value"),
           "it doesn't add the value to the list of values"
         );
 
@@ -91,8 +96,9 @@ discourseModule(
 
         await click(".values .value[data-index='0'] .remove-value-btn");
 
-        assert.ok(
-          queryAll(".values .value").length === 1,
+        assert.equal(
+          count(".values .value"),
+          1,
           "it removes the value from the list of values"
         );
 
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 1dabaf5c206..73c421bbe47 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
@@ -3,8 +3,10 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
+  query,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
@@ -20,29 +22,30 @@ discourseModule("Integration | Component | simple-list", function (hooks) {
 
     async test(assert) {
       assert.ok(
-        queryAll(".add-value-btn[disabled]").length,
+        exists(".add-value-btn[disabled]"),
         "while loading the + button is disabled"
       );
 
       await fillIn(".add-value-input", "penar");
       await click(".add-value-btn");
 
-      assert.ok(
-        queryAll(".values .value").length === 3,
+      assert.equal(
+        count(".values .value"),
+        3,
         "it adds the value to the list of values"
       );
 
       assert.ok(
-        queryAll(".values .value[data-index='2'] .value-input")[0].value ===
-          "penar",
+        query(".values .value[data-index='2'] .value-input").value === "penar",
         "it sets the correct value for added item"
       );
 
       await fillIn(".add-value-input", "eviltrout");
       await triggerKeyEvent(".add-value-input", "keydown", 13); // enter
 
-      assert.ok(
-        queryAll(".values .value").length === 4,
+      assert.equal(
+        count(".values .value"),
+        4,
         "it adds the value when keying Enter"
       );
     },
@@ -58,14 +61,14 @@ discourseModule("Integration | Component | simple-list", function (hooks) {
     async test(assert) {
       await click(".values .value[data-index='0'] .remove-value-btn");
 
-      assert.ok(
-        queryAll(".values .value").length === 1,
+      assert.equal(
+        count(".values .value"),
+        1,
         "it removes the value from the list of values"
       );
 
       assert.ok(
-        queryAll(".values .value[data-index='0'] .value-input")[0].value ===
-          "osama",
+        query(".values .value[data-index='0'] .value-input").value === "osama",
         "it removes the correct value"
       );
     },
@@ -82,13 +85,14 @@ discourseModule("Integration | Component | simple-list", function (hooks) {
       await fillIn(".add-value-input", "eviltrout");
       await click(".add-value-btn");
 
-      assert.ok(
-        queryAll(".values .value").length === 3,
+      assert.equal(
+        count(".values .value"),
+        3,
         "it adds the value to the list of values"
       );
 
       assert.ok(
-        queryAll(".values .value[data-index='2'] .value-input")[0].value ===
+        query(".values .value[data-index='2'] .value-input").value ===
           "eviltrout",
         "it adds the correct value"
       );
diff --git a/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js b/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js
index 73eacaf5f5d..cf33a76b3ac 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/site-header-test.js
@@ -2,8 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
 } from "discourse/tests/helpers/qunit-helpers";
 import pretender from "discourse/tests/helpers/create-pretender";
 import hbs from "htmlbars-inline-precompile";
@@ -20,8 +21,9 @@ discourseModule("Integration | Component | site-header", function (hooks) {
     },
 
     async test(assert) {
-      assert.ok(
-        queryAll(".ring-backdrop").length === 1,
+      assert.equal(
+        count(".ring-backdrop"),
+        1,
         "there is the first notification mask"
       );
 
@@ -29,7 +31,7 @@ discourseModule("Integration | Component | site-header", function (hooks) {
       await click("header.d-header");
 
       assert.ok(
-        queryAll(".ring-backdrop").length === 0,
+        !exists(".ring-backdrop"),
         "it hides the first notification mask"
       );
     },
@@ -41,7 +43,7 @@ discourseModule("Integration | Component | site-header", function (hooks) {
 
     async test(assert) {
       assert.ok(
-        queryAll(".ring-backdrop").length === 0,
+        !exists(".ring-backdrop"),
         "there is no first notification mask for anonymous users"
       );
 
diff --git a/app/assets/javascripts/discourse/tests/integration/components/slow-mode-info-test.js b/app/assets/javascripts/discourse/tests/integration/components/slow-mode-info-test.js
index 25f302b0343..63204bdf2a3 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/slow-mode-info-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/slow-mode-info-test.js
@@ -2,9 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
   exists,
-  queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
@@ -43,7 +43,7 @@ discourseModule("Integration | Component | slow-mode-info", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll(".slow-mode-heading").length === 1);
+      assert.equal(count(".slow-mode-heading"), 1);
     },
   });
 
@@ -58,7 +58,7 @@ discourseModule("Integration | Component | slow-mode-info", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll(".slow-mode-remove").length === 1);
+      assert.equal(count(".slow-mode-remove"), 1);
     },
   });
 
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 91b419be434..dc89583291c 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
@@ -3,6 +3,7 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import I18n from "I18n";
@@ -17,7 +18,7 @@ discourseModule("Integration | Component | text-field", function (hooks) {
     template: hbs`{{text-field}}`,
 
     test(assert) {
-      assert.ok(queryAll("input[type=text]").length);
+      assert.ok(exists("input[type=text]"));
     },
   });
 
@@ -29,7 +30,7 @@ discourseModule("Integration | Component | text-field", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll("input[type=text]").length);
+      assert.ok(exists("input[type=text]"));
       assert.equal(
         queryAll("input").prop("placeholder"),
         "placeholder.i18n.key"
diff --git a/app/assets/javascripts/discourse/tests/integration/components/themes-list-item-test.js b/app/assets/javascripts/discourse/tests/integration/components/themes-list-item-test.js
index 0dc7fea9c44..56c517c5beb 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/themes-list-item-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/themes-list-item-test.js
@@ -4,6 +4,7 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -19,11 +20,7 @@ discourseModule("Integration | Component | themes-list-item", function (hooks) {
 
     test(assert) {
       assert.expect(1);
-      assert.equal(
-        queryAll(".d-icon-check").length,
-        1,
-        "shows default theme icon"
-      );
+      assert.equal(count(".d-icon-check"), 1, "shows default theme icon");
     },
   });
 
@@ -38,11 +35,7 @@ discourseModule("Integration | Component | themes-list-item", function (hooks) {
 
     test(assert) {
       assert.expect(1);
-      assert.equal(
-        queryAll(".d-icon-sync").length,
-        1,
-        "shows pending update icon"
-      );
+      assert.equal(count(".d-icon-sync"), 1, "shows pending update icon");
     },
   });
 
@@ -61,7 +54,7 @@ discourseModule("Integration | Component | themes-list-item", function (hooks) {
     test(assert) {
       assert.expect(1);
       assert.equal(
-        queryAll(".d-icon-exclamation-circle").length,
+        count(".d-icon-exclamation-circle"),
         1,
         "shows broken theme icon"
       );
diff --git a/app/assets/javascripts/discourse/tests/integration/components/themes-list-test.js b/app/assets/javascripts/discourse/tests/integration/components/themes-list-test.js
index 3ab035c5f3d..375340ae6e6 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/themes-list-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/themes-list-test.js
@@ -4,6 +4,7 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -49,11 +50,7 @@ discourseModule("Integration | Component | themes-list", function (hooks) {
         -1,
         "there is no inactive themes separator when all themes are inactive"
       );
-      assert.equal(
-        queryAll(".themes-list-item").length,
-        5,
-        "displays all themes"
-      );
+      assert.equal(count(".themes-list-item"), 5, "displays all themes");
 
       [2, 3].forEach((num) => this.themes[num].set("user_selectable", true));
       this.themes[4].set("default", true);
@@ -82,7 +79,7 @@ discourseModule("Integration | Component | themes-list", function (hooks) {
 
       this.set("themes", []);
       assert.equal(
-        queryAll(".themes-list-item").length,
+        count(".themes-list-item"),
         1,
         "shows one entry with a message when there is nothing to display"
       );
@@ -132,15 +129,11 @@ discourseModule("Integration | Component | themes-list", function (hooks) {
         -1,
         "there is no separator"
       );
-      assert.equal(
-        queryAll(".themes-list-item").length,
-        5,
-        "displays all components"
-      );
+      assert.equal(count(".themes-list-item"), 5, "displays all components");
 
       this.set("components", []);
       assert.equal(
-        queryAll(".themes-list-item").length,
+        count(".themes-list-item"),
         1,
         "shows one entry with a message when there is nothing to display"
       );
diff --git a/app/assets/javascripts/discourse/tests/integration/components/user-avatar-flair-test.js b/app/assets/javascripts/discourse/tests/integration/components/user-avatar-flair-test.js
index 7b81e20913e..81900086cd2 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/user-avatar-flair-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/user-avatar-flair-test.js
@@ -3,6 +3,7 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
@@ -61,8 +62,8 @@ discourseModule(
         resetFlair();
       },
       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.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(exists("svg.d-icon-bars"), "it has the svg icon");
         assert.equal(
           queryAll(".avatar-flair").attr("style"),
           "background-color: #CC000A; color: #FFFFFA; ",
@@ -86,8 +87,8 @@ discourseModule(
         resetFlair();
       },
       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.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(exists("svg.d-icon-bars"), "it has the svg icon");
         assert.equal(
           queryAll(".avatar-flair").attr("style"),
           "background-color: #CC0005; color: #FFFFF5; ",
@@ -111,11 +112,8 @@ discourseModule(
         resetFlair();
       },
       test(assert) {
-        assert.ok(queryAll(".avatar-flair").length, "it has the tag");
-        assert.ok(
-          queryAll("svg.d-icon-dice-two").length,
-          "it has the svg icon"
-        );
+        assert.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(exists("svg.d-icon-dice-two"), "it has the svg icon");
         assert.equal(
           queryAll(".avatar-flair").attr("style"),
           "background-color: #CC0002; color: #FFFFF2; ",
@@ -139,11 +137,8 @@ discourseModule(
         resetFlair();
       },
       test(assert) {
-        assert.ok(queryAll(".avatar-flair").length, "it has the tag");
-        assert.ok(
-          queryAll("svg.d-icon-dice-two").length,
-          "it has the svg icon"
-        );
+        assert.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(exists("svg.d-icon-dice-two"), "it has the svg icon");
         assert.equal(
           queryAll(".avatar-flair").attr("style"),
           "background-color: #CC0002; color: #FFFFF2; ",
@@ -171,8 +166,8 @@ discourseModule(
         resetFlair();
       },
       test(assert) {
-        assert.ok(queryAll(".avatar-flair").length, "it has the tag");
-        assert.ok(queryAll("svg.d-icon-times").length, "it has the svg icon");
+        assert.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(exists("svg.d-icon-times"), "it has the svg icon");
         assert.equal(
           queryAll(".avatar-flair").attr("style"),
           "background-color: #123456; color: #B0B0B0; ",
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 f22fc3b17c1..9ca7a9be2f4 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,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, query } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
 function paste(element, text) {
@@ -24,7 +21,7 @@ discourseModule("Integration | Component | user-selector", function (hooks) {
     },
 
     test(assert) {
-      let element = queryAll(".test-selector")[0];
+      let element = query(".test-selector");
 
       assert.equal(this.get("usernames"), "evil,trout");
       paste(element, "zip,zap,zoom");
@@ -55,7 +52,7 @@ discourseModule("Integration | Component | user-selector", function (hooks) {
     },
 
     test(assert) {
-      let element = queryAll(".test-selector")[0];
+      let element = query(".test-selector");
       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 cd02aa0a20b..3c2e3520ed1 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
@@ -2,8 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  query,
 } from "discourse/tests/helpers/qunit-helpers";
 import { click } from "@ember/test-helpers";
 import hbs from "htmlbars-inline-precompile";
@@ -26,8 +27,9 @@ discourseModule("Integration | Component | value-list", function (hooks) {
       await selectKit().fillInFilter("eviltrout");
       await selectKit().keyboard("enter");
 
-      assert.ok(
-        queryAll(".values .value").length === 3,
+      assert.equal(
+        count(".values .value"),
+        3,
         "it adds the value to the list of values"
       );
 
@@ -49,8 +51,9 @@ discourseModule("Integration | Component | value-list", function (hooks) {
     async test(assert) {
       await click(".values .value[data-index='0'] .remove-value-btn");
 
-      assert.ok(
-        queryAll(".values .value").length === 1,
+      assert.equal(
+        count(".values .value"),
+        1,
         "it removes the value from the list of values"
       );
 
@@ -59,7 +62,7 @@ discourseModule("Integration | Component | value-list", function (hooks) {
       await selectKit().expand();
 
       assert.ok(
-        queryAll(".select-kit-collection li.select-kit-row span.name")[0]
+        query(".select-kit-collection li.select-kit-row span.name")
           .innerText === "vinkas",
         "it adds the removed value to choices"
       );
@@ -80,8 +83,9 @@ discourseModule("Integration | Component | value-list", function (hooks) {
       await selectKit().expand();
       await selectKit().selectRowByValue("maja");
 
-      assert.ok(
-        queryAll(".values .value").length === 3,
+      assert.equal(
+        count(".values .value"),
+        3,
         "it adds the value to the list of values"
       );
 
@@ -107,8 +111,9 @@ discourseModule("Integration | Component | value-list", function (hooks) {
       await selectKit().fillInFilter("eviltrout");
       await selectKit().keyboard("enter");
 
-      assert.ok(
-        queryAll(".values .value").length === 3,
+      assert.equal(
+        count(".values .value"),
+        3,
         "it adds the value to the list of values"
       );
 
@@ -134,8 +139,9 @@ discourseModule("Integration | Component | value-list", function (hooks) {
       await selectKit().fillInFilter("eviltrout");
       await selectKit().keyboard("enter");
 
-      assert.ok(
-        queryAll(".values .value").length === 3,
+      assert.equal(
+        count(".values .value"),
+        3,
         "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 a7ff62bfab8..0b036d76cbf 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,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { count, discourseModule } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
 discourseModule(
@@ -22,12 +19,14 @@ discourseModule(
         });
       },
       test(assert) {
-        assert.ok(
-          queryAll(".post-action .d-icon-far-trash-alt").length === 1,
+        assert.equal(
+          count(".post-action .d-icon-far-trash-alt"),
+          1,
           "it has the deleted icon"
         );
-        assert.ok(
-          queryAll(".avatar[title=eviltrout]").length === 1,
+        assert.equal(
+          count(".avatar[title=eviltrout]"),
+          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 fd3bb7ac7df..1ed63b8bc21 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
@@ -3,6 +3,7 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
@@ -22,8 +23,8 @@ discourseModule(
         });
       },
       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.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(exists("svg.d-icon-bars"), "it has the svg icon");
         assert.equal(
           queryAll(".avatar-flair").attr("style"),
           "background-color: #CC0000; color: #FFFFFF; ",
@@ -40,8 +41,8 @@ discourseModule(
         });
       },
       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");
+        assert.ok(exists(".avatar-flair"), "it has the tag");
+        assert.ok(!exists("svg"), "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 2606de6fbc9..c8b5bf9f7c5 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/button-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/button-test.js
@@ -3,8 +3,8 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
   query,
-  queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
@@ -20,13 +20,10 @@ discourseModule("Integration | Component | Widget | button", function (hooks) {
 
     test(assert) {
       assert.ok(
-        queryAll("button.btn.btn-icon.no-text").length,
+        exists("button.btn.btn-icon.no-text"),
         "it has all the classes"
       );
-      assert.ok(
-        queryAll("button .d-icon.d-icon-far-smile").length,
-        "it has the icon"
-      );
+      assert.ok(exists("button .d-icon.d-icon-far-smile"), "it has the icon");
     },
   });
 
@@ -38,18 +35,9 @@ discourseModule("Integration | Component | Widget | button", function (hooks) {
     },
 
     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"
-      );
+      assert.ok(exists("button.btn.btn-icon-text"), "it has all the classes");
+      assert.ok(exists("button .d-icon.d-icon-plus"), "it has the icon");
+      assert.ok(exists("button span.d-button-label"), "it has the label");
     },
   });
 
@@ -61,14 +49,8 @@ discourseModule("Integration | Component | Widget | button", function (hooks) {
     },
 
     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"
-      );
+      assert.ok(exists("button.btn.btn-text"), "it has all the classes");
+      assert.ok(exists("button span.d-button-label"), "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 7136ca23b51..6b09fb28892 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
@@ -2,8 +2,10 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
+  query,
 } from "discourse/tests/helpers/qunit-helpers";
 import EmberObject from "@ember/object";
 import hbs from "htmlbars-inline-precompile";
@@ -59,18 +61,18 @@ discourseModule(
           ];
         });
 
-        assert.equal(queryAll("li.read").length, 0);
+        assert.ok(!exists("li.read"));
 
         $(document).trigger(
           $.Event("mouseup", {
-            target: queryAll("li")[0],
+            target: query("li"),
             button: 1,
             which: 2,
           })
         );
         await settled();
 
-        assert.equal(queryAll("li.read").length, 1);
+        assert.equal(count("li.read"), 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 b07bdd9c5c5..fde73beabb7 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
@@ -2,7 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import { NotificationLevels } from "discourse/lib/notification-levels";
@@ -27,8 +29,8 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll(".faq-priority").length);
-        assert.ok(!queryAll(".faq-link").length);
+        assert.ok(exists(".faq-priority"));
+        assert.ok(!exists(".faq-link"));
       },
     });
 
@@ -41,8 +43,8 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(!queryAll(".faq-priority").length);
-        assert.ok(queryAll(".faq-link").length);
+        assert.ok(!exists(".faq-priority"));
+        assert.ok(exists(".faq-link"));
       },
     });
 
@@ -54,7 +56,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(!queryAll(".admin-link").length);
+        assert.ok(!exists(".admin-link"));
       },
     });
 
@@ -67,9 +69,9 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll(".admin-link").length);
-        assert.ok(queryAll(".review").length);
-        assert.ok(!queryAll(".settings-link").length);
+        assert.ok(exists(".admin-link"));
+        assert.ok(exists(".review"));
+        assert.ok(!exists(".settings-link"));
       },
     });
 
@@ -81,7 +83,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll(".settings-link").length);
+        assert.ok(exists(".settings-link"));
       },
     });
 
@@ -89,8 +91,8 @@ discourseModule(
       template: hbs`{{mount-widget widget="hamburger-menu"}}`,
 
       test(assert) {
-        assert.ok(queryAll(".new-topics-link").length);
-        assert.ok(queryAll(".unread-topics-link").length);
+        assert.ok(exists(".new-topics-link"));
+        assert.ok(exists(".unread-topics-link"));
       },
     });
 
@@ -99,13 +101,13 @@ discourseModule(
       anonymous: true,
 
       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);
+        assert.ok(!exists("li[class='']"));
+        assert.ok(exists(".latest-topics-link"));
+        assert.ok(!exists(".new-topics-link"));
+        assert.ok(!exists(".unread-topics-link"));
+        assert.ok(exists(".top-topics-link"));
+        assert.ok(exists(".badge-link"));
+        assert.ok(exists(".category-link"));
       },
     });
 
@@ -120,7 +122,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.equal(queryAll(".category-link").length, 8);
+        assert.equal(count(".category-link"), 8);
         assert.equal(
           queryAll(".category-link .category-name").text(),
           this.site
@@ -142,7 +144,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.equal(queryAll(".category-link").length, 8);
+        assert.equal(count(".category-link"), 8);
         assert.equal(
           queryAll(".category-link .category-name").text(),
           this.site
@@ -198,7 +200,7 @@ discourseModule(
 
       test(assert) {
         assert.equal(
-          queryAll(".category-link").length,
+          count(".category-link"),
           maxCategoriesToDisplay,
           "categories displayed limited by header_dropdown_category_count"
         );
@@ -235,7 +237,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(!queryAll(".badge-link").length);
+        assert.ok(!exists(".badge-link"));
       },
     });
 
@@ -243,7 +245,7 @@ discourseModule(
       template: hbs`{{mount-widget widget="hamburger-menu"}}`,
 
       test(assert) {
-        assert.ok(queryAll(".badge-link").length);
+        assert.ok(exists(".badge-link"));
       },
     });
 
@@ -251,7 +253,7 @@ discourseModule(
       template: hbs`{{mount-widget widget="hamburger-menu"}}`,
 
       test(assert) {
-        assert.ok(queryAll(".user-directory-link").length);
+        assert.ok(exists(".user-directory-link"));
       },
     });
 
@@ -263,7 +265,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(!queryAll(".user-directory-link").length);
+        assert.ok(!exists(".user-directory-link"));
       },
     });
 
@@ -271,8 +273,8 @@ discourseModule(
       template: hbs`{{mount-widget widget="hamburger-menu"}}`,
 
       test(assert) {
-        assert.ok(queryAll(".about-link").length);
-        assert.ok(queryAll(".keyboard-shortcuts-link").length);
+        assert.ok(exists(".about-link"));
+        assert.ok(exists(".keyboard-shortcuts-link"));
       },
     });
   }
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 c099e69261b..433ace45ca5 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/header-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/header-test.js
@@ -1,11 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  exists,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers";
 import { click } from "@ember/test-helpers";
 import hbs from "htmlbars-inline-precompile";
 
@@ -15,8 +11,8 @@ discourseModule("Integration | Component | Widget | header", function (hooks) {
   componentTest("rendering basics", {
     template: hbs`{{mount-widget widget="header"}}`,
     test(assert) {
-      assert.ok(queryAll("header.d-header").length);
-      assert.ok(queryAll("#site-logo").length);
+      assert.ok(exists("header.d-header"));
+      assert.ok(exists("#site-logo"));
     },
   });
 
@@ -38,8 +34,8 @@ discourseModule("Integration | Component | Widget | header", function (hooks) {
     },
 
     async test(assert) {
-      assert.ok(queryAll("button.sign-up-button").length);
-      assert.ok(queryAll("button.login-button").length);
+      assert.ok(exists("button.sign-up-button"));
+      assert.ok(exists("button.login-button"));
 
       await click("button.sign-up-button");
       assert.ok(this.signupShown);
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 41992f056f4..ca23507ac78 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
@@ -2,7 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import Session from "discourse/models/session";
@@ -31,9 +33,9 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll(".title").length === 1);
+        assert.equal(count(".title"), 1);
 
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(queryAll("#site-logo").attr("src"), bigLogo);
         assert.equal(queryAll("#site-logo").attr("alt"), title);
       },
@@ -49,7 +51,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("img.logo-small").length === 1);
+        assert.equal(count("img.logo-small"), 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);
@@ -66,7 +68,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("h1#site-text-logo.text-logo").length === 1);
+        assert.equal(count("h1#site-text-logo.text-logo"), 1);
         assert.equal(queryAll("#site-text-logo").text(), title);
       },
     });
@@ -81,7 +83,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll(".d-icon-home").length === 1);
+        assert.equal(count(".d-icon-home"), 1);
       },
     });
 
@@ -94,7 +96,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-mobile").length === 1);
+        assert.equal(count("img#site-logo.logo-mobile"), 1);
         assert.equal(queryAll("#site-logo").attr("src"), mobileLogo);
       },
     });
@@ -107,7 +109,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(queryAll("#site-logo").attr("src"), bigLogo);
       },
     });
@@ -124,7 +126,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(queryAll("#site-logo").attr("src"), bigLogo);
 
         assert.equal(
@@ -182,12 +184,9 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(queryAll("#site-logo").attr("src"), bigLogo);
-        assert.ok(
-          queryAll("picture").length === 0,
-          "does not include alternative logo"
-        );
+        assert.ok(!exists("picture"), "does not include alternative logo");
       },
     });
 
@@ -199,12 +198,9 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(queryAll("#site-logo").attr("src"), bigLogo);
-        assert.ok(
-          queryAll("picture").length === 0,
-          "does not include alternative logo"
-        );
+        assert.ok(!exists("picture"), "does not include alternative logo");
       },
     });
 
@@ -219,16 +215,13 @@ discourseModule(
         Session.currentProp("defaultColorSchemeIsDark", null);
       },
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(
           queryAll("#site-logo").attr("src"),
           darkLogo,
           "uses dark logo"
         );
-        assert.ok(
-          queryAll("picture").length === 0,
-          "does not add dark mode alternative"
-        );
+        assert.ok(!exists("picture"), "does not add dark mode alternative");
       },
     });
 
@@ -243,7 +236,7 @@ discourseModule(
         Session.currentProp("defaultColorSchemeIsDark", null);
       },
       test(assert) {
-        assert.ok(queryAll("img#site-logo.logo-big").length === 1);
+        assert.equal(count("img#site-logo.logo-big"), 1);
         assert.equal(
           queryAll("#site-logo").attr("src"),
           bigLogo,
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 614ff999b90..e1d56b45083 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,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { count, discourseModule } from "discourse/tests/helpers/qunit-helpers";
 import { click } from "@ember/test-helpers";
 import hbs from "htmlbars-inline-precompile";
 
@@ -34,7 +31,7 @@ discourseModule(
       },
       test(assert) {
         assert.equal(
-          queryAll(".post-links a.track-link").length,
+          count(".post-links a.track-link"),
           1,
           "it hides the dupe link"
         );
@@ -86,12 +83,9 @@ discourseModule(
         });
       },
       async test(assert) {
-        assert.ok(
-          queryAll(".expand-links").length === 1,
-          "collapsed by default"
-        );
+        assert.equal(count(".expand-links"), 1, "collapsed by default");
         await click("a.expand-links");
-        assert.equal(queryAll(".post-links a.track-link").length, 7);
+        assert.equal(count(".post-links a.track-link"), 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 1e79fae7cae..4c03c0c8f32 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
@@ -2,8 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 import { withPluginApi } from "discourse/lib/plugin-api";
@@ -30,8 +31,9 @@ discourseModule(
         });
       },
       async test(assert) {
-        assert.ok(
-          queryAll(".actions .extra-buttons .hot-coffee").length === 1,
+        assert.equal(
+          count(".actions .extra-buttons .hot-coffee"),
+          1,
           "It renders extra button"
         );
       },
@@ -47,7 +49,7 @@ discourseModule(
       },
       async test(assert) {
         assert.ok(
-          queryAll(".actions .extra-buttons .hot-coffee").length === 0,
+          !exists(".actions .extra-buttons .hot-coffee"),
           "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 b4118cccb4e..eb3a5ba6caf 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
@@ -2,6 +2,7 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
@@ -72,8 +73,8 @@ discourseModule(
       },
 
       test(assert) {
-        assert.equal(queryAll(".post-stream").length, 1);
-        assert.equal(queryAll(".topic-post").length, 6, "renders all posts");
+        assert.equal(count(".post-stream"), 1);
+        assert.equal(count(".topic-post"), 6, "renders all posts");
 
         // look for special class bindings
         assert.equal(
@@ -113,11 +114,11 @@ discourseModule(
         );
 
         // 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(count("article#post_2"), 1);
+        assert.equal(count('article[data-user-id="123"]'), 1);
+        assert.equal(count('article[data-post-id="3"]'), 1);
+        assert.equal(count("article#post_5.via-email"), 1);
+        assert.equal(count("article#post_6.is-auto-generated"), 1);
 
         assert.equal(
           queryAll("article:nth-of-type(1) .main-avatar").length,
@@ -143,12 +144,12 @@ discourseModule(
 
       test(assert) {
         assert.equal(
-          queryAll(".topic-post.deleted").length,
+          count(".topic-post.deleted"),
           1,
           "it applies the deleted class"
         );
         assert.equal(
-          queryAll(".deleted-user-avatar").length,
+          count(".deleted-user-avatar"),
           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 995ec592ad8..4598e485795 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/post-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/post-test.js
@@ -2,7 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import EmberObject from "@ember/object";
@@ -19,11 +21,11 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { shareUrl: "/example", post_number: 1 });
     },
     test(assert) {
-      assert.ok(queryAll(".names").length, "includes poster name");
+      assert.ok(exists(".names"), "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(exists("a.post-date"), "includes post date");
+      assert.ok(exists("a.post-date[data-share-url]"));
+      assert.ok(exists("a.post-date[data-post-number]"));
     },
   });
 
@@ -147,8 +149,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { isWhisper: true });
     },
     test(assert) {
-      assert.ok(queryAll(".topic-post.whisper").length === 1);
-      assert.ok(queryAll(".post-info.whisper").length === 1);
+      assert.equal(count(".topic-post.whisper"), 1);
+      assert.equal(count(".post-info.whisper"), 1);
     },
   });
 
@@ -167,18 +169,18 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { likeCount: 1 });
     },
     async test(assert) {
-      assert.ok(queryAll("button.like-count").length === 1);
-      assert.ok(queryAll(".who-liked").length === 0);
+      assert.equal(count("button.like-count"), 1);
+      assert.ok(!exists(".who-liked"));
 
       // 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);
+      assert.equal(count(".who-liked"), 1);
+      assert.equal(count(".who-liked a.trigger-user-card"), 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);
+      assert.ok(!exists(".who-liked"));
+      assert.ok(!exists(".who-liked a.trigger-user-card"));
     },
   });
 
@@ -188,7 +190,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { likeCount: 0 });
     },
     test(assert) {
-      assert.ok(queryAll("button.like-count").length === 0);
+      assert.ok(!exists("button.like-count"));
     },
   });
 
@@ -199,7 +201,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
     },
     test(assert) {
       assert.ok(
-        !!queryAll(".actions button[data-share-url]").length,
+        exists(".actions button[data-share-url]"),
         "it renders a share button"
       );
     },
@@ -218,18 +220,18 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     async test(assert) {
-      assert.ok(!!queryAll(".actions button.like").length);
-      assert.ok(queryAll(".actions button.like-count").length === 0);
+      assert.ok(exists(".actions button.like"));
+      assert.ok(!exists(".actions button.like-count"));
 
       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);
+      assert.ok(!exists(".actions button.like"));
+      assert.ok(exists(".actions button.has-like"));
+      assert.equal(count(".actions button.like-count"), 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);
+      assert.ok(exists(".actions button.like"));
+      assert.ok(!exists(".actions button.has-like"));
+      assert.ok(!exists(".actions button.like-count"));
     },
   });
 
@@ -244,8 +246,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("showLogin", () => (this.loginShown = true));
     },
     async test(assert) {
-      assert.ok(!!queryAll(".actions button.like").length);
-      assert.ok(queryAll(".actions button.like-count").length === 0);
+      assert.ok(exists(".actions button.like"));
+      assert.ok(!exists(".actions button.like-count"));
 
       assert.equal(
         queryAll("button.like").attr("title"),
@@ -278,11 +280,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canEdit: false });
     },
     test(assert) {
-      assert.equal(
-        queryAll("button.edit").length,
-        0,
-        `button is not displayed`
-      );
+      assert.ok(!exists("button.edit"), "button is not displayed");
     },
   });
 
@@ -320,11 +318,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canDeleteTopic: false });
     },
     test(assert) {
-      assert.equal(
-        queryAll("button.delete").length,
-        0,
-        `button is not displayed`
-      );
+      assert.ok(!exists("button.delete"), `button is not displayed`);
     },
   });
 
@@ -343,16 +337,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       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(count("button.create-flag"), 1, `button is displayed`);
+        assert.equal(count("button.delete"), 1, `button is displayed`);
         assert.equal(
           queryAll("button.delete").attr("title"),
           I18n.t("post.controls.delete_topic_disallowed"),
@@ -382,11 +368,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canRecoverTopic: false });
     },
     test(assert) {
-      assert.equal(
-        queryAll("button.recover").length,
-        0,
-        `button is not displayed`
-      );
+      assert.ok(!exists("button.recover"), `button is not displayed`);
     },
   });
 
@@ -411,11 +393,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canDelete: false });
     },
     test(assert) {
-      assert.equal(
-        queryAll("button.delete").length,
-        0,
-        `button is not displayed`
-      );
+      assert.ok(!exists("button.delete"), `button is not displayed`);
     },
   });
 
@@ -429,16 +407,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     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`
-      );
+      assert.ok(!exists("button.delete"), `delete button is not displayed`);
+      assert.ok(!exists("button.create-flag"), `flag button is not displayed`);
     },
   });
 
@@ -462,11 +432,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canRecover: false });
     },
     test(assert) {
-      assert.equal(
-        queryAll("button.recover").length,
-        0,
-        `button is not displayed`
-      );
+      assert.ok(!exists("button.recover"), `button is not displayed`);
     },
   });
 
@@ -479,7 +445,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("showFlags", () => (this.flagsShown = true));
     },
     async test(assert) {
-      assert.ok(queryAll("button.create-flag").length === 1);
+      assert.equal(count("button.create-flag"), 1);
 
       await click("button.create-flag");
       assert.ok(this.flagsShown, "it triggered the action");
@@ -492,7 +458,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canFlag: false });
     },
     test(assert) {
-      assert.ok(queryAll("button.create-flag").length === 0);
+      assert.ok(!exists("button.create-flag"));
     },
   });
 
@@ -502,7 +468,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canFlag: true, hidden: true });
     },
     test(assert) {
-      assert.ok(queryAll("button.create-flag").length === 0);
+      assert.ok(!exists("button.create-flag"));
     },
   });
 
@@ -512,7 +478,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { read: true });
     },
     test(assert) {
-      assert.ok(queryAll(".read-state.read").length);
+      assert.ok(exists(".read-state.read"));
     },
   });
 
@@ -522,7 +488,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { read: false });
     },
     test(assert) {
-      assert.ok(queryAll(".read-state").length);
+      assert.ok(exists(".read-state"));
     },
   });
 
@@ -536,12 +502,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     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"
-      );
+      assert.ok(!exists("a.reply-to-tab"), "hides the tab");
+      assert.ok(!exists(".avoid-tab"), "doesn't have the avoid tab class");
     },
   });
 
@@ -555,8 +517,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     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");
+      assert.ok(exists("a.reply-to-tab"), "shows the tab");
+      assert.equal(count(".avoid-tab"), 1, "has the avoid tab class");
     },
   });
 
@@ -571,13 +533,10 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.siteSettings.suppress_reply_directly_above = false;
     },
     async test(assert) {
-      assert.equal(queryAll(".avoid-tab").length, 1, "has the avoid tab class");
+      assert.equal(count(".avoid-tab"), 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
-      );
+      assert.equal(count("section.embedded-posts.top .cooked"), 1);
+      assert.equal(count("section.embedded-posts .d-icon-arrow-up"), 1);
     },
   });
 
@@ -603,7 +562,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
     },
     async test(assert) {
       await click(".topic-body .expand-post");
-      assert.equal(queryAll(".expand-post").length, 0, "button is gone");
+      assert.ok(!exists(".expand-post"), "button is gone");
     },
   });
 
@@ -613,8 +572,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canBookmark: false });
     },
     test(assert) {
-      assert.equal(queryAll("button.bookmark").length, 0);
-      assert.equal(queryAll("button.bookmarked").length, 0);
+      assert.ok(!exists("button.bookmark"));
+      assert.ok(!exists("button.bookmarked"));
     },
   });
 
@@ -629,8 +588,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("toggleBookmark", () => (args.bookmarked = true));
     },
     async test(assert) {
-      assert.equal(queryAll(".post-menu-area .bookmark").length, 1);
-      assert.equal(queryAll("button.bookmarked").length, 0);
+      assert.equal(count(".post-menu-area .bookmark"), 1);
+      assert.ok(!exists("button.bookmarked"));
     },
   });
 
@@ -640,7 +599,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canManage: false });
     },
     test(assert) {
-      assert.equal(queryAll(".post-menu-area .show-post-admin-menu").length, 0);
+      assert.ok(!exists(".post-menu-area .show-post-admin-menu"));
     },
   });
 
@@ -650,17 +609,12 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canManage: true });
     },
     async test(assert) {
-      assert.equal(queryAll(".post-admin-menu").length, 0);
+      assert.ok(!exists(".post-admin-menu"));
       await click(".post-menu-area .show-post-admin-menu");
-      assert.equal(
-        queryAll(".post-admin-menu").length,
-        1,
-        "it shows the popup"
-      );
+      assert.equal(count(".post-admin-menu"), 1, "it shows the popup");
       await click(".post-menu-area");
-      assert.equal(
-        queryAll(".post-admin-menu").length,
-        0,
+      assert.ok(
+        !exists(".post-admin-menu"),
         "clicking outside clears the popup"
       );
     },
@@ -680,11 +634,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       await click(".post-admin-menu .toggle-post-type");
 
       assert.ok(this.toggled);
-      assert.equal(
-        queryAll(".post-admin-menu").length,
-        0,
-        "also hides the menu"
-      );
+      assert.ok(!exists(".post-admin-menu"), "also hides the menu");
     },
   });
   componentTest("toggle moderator post", {
@@ -701,11 +651,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       await click(".post-admin-menu .toggle-post-type");
 
       assert.ok(this.toggled);
-      assert.equal(
-        queryAll(".post-admin-menu").length,
-        0,
-        "also hides the menu"
-      );
+      assert.ok(!exists(".post-admin-menu"), "also hides the menu");
     },
   });
 
@@ -721,11 +667,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       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"
-      );
+      assert.ok(!exists(".post-admin-menu"), "also hides the menu");
     },
   });
 
@@ -742,11 +684,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       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"
-      );
+      assert.ok(!exists(".post-admin-menu"), "also hides the menu");
     },
   });
 
@@ -763,11 +701,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       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"
-      );
+      assert.ok(!exists(".post-admin-menu"), "also hides the menu");
     },
   });
 
@@ -791,7 +725,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { canCreatePost: false });
     },
     test(assert) {
-      assert.equal(queryAll(".post-controls .create").length, 0);
+      assert.ok(!exists(".post-controls .create"));
     },
   });
 
@@ -801,7 +735,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { replyCount: 0 });
     },
     test(assert) {
-      assert.equal(queryAll("button.show-replies").length, 0);
+      assert.ok(!exists("button.show-replies"));
     },
   });
 
@@ -812,7 +746,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { replyCount: 2, replyDirectlyBelow: true });
     },
     test(assert) {
-      assert.equal(queryAll("button.show-replies").length, 1);
+      assert.equal(count("button.show-replies"), 1);
     },
   });
 
@@ -823,7 +757,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { replyCount: 1, replyDirectlyBelow: true });
     },
     test(assert) {
-      assert.equal(queryAll("button.show-replies").length, 0);
+      assert.ok(!exists("button.show-replies"));
     },
   });
 
@@ -835,11 +769,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
     },
     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
-      );
+      assert.equal(count("section.embedded-posts.bottom .cooked"), 1);
+      assert.equal(count("section.embedded-posts .d-icon-arrow-down"), 1);
     },
   });
 
@@ -849,7 +780,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { showTopicMap: false });
     },
     test(assert) {
-      assert.equal(queryAll(".topic-map").length, 0);
+      assert.ok(!exists(".topic-map"));
     },
   });
 
@@ -863,15 +794,14 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     async test(assert) {
-      assert.equal(
-        queryAll("li.avatars a.poster").length,
-        0,
+      assert.ok(
+        !exists("li.avatars a.poster"),
         "shows no participants when collapsed"
       );
 
       await click("nav.buttons button");
       assert.equal(
-        queryAll(".topic-map-expanded a.poster").length,
+        count(".topic-map-expanded a.poster"),
         2,
         "shows all when expanded"
       );
@@ -895,19 +825,19 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
     },
     async test(assert) {
       assert.equal(
-        queryAll("li.avatars a.poster").length,
+        count("li.avatars a.poster"),
         3,
         "limits to three participants"
       );
 
       await click("nav.buttons button");
-      assert.equal(queryAll("li.avatars a.poster").length, 0);
+      assert.ok(!exists("li.avatars a.poster"));
       assert.equal(
-        queryAll(".topic-map-expanded a.poster").length,
+        count(".topic-map-expanded a.poster"),
         4,
         "shows all when expanded"
       );
-      assert.equal(queryAll("a.poster.toggled").length, 2, "two are toggled");
+      assert.equal(count("a.poster.toggled"), 2, "two are toggled");
     },
   });
 
@@ -927,23 +857,23 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     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);
+      assert.equal(count(".topic-map"), 1);
+      assert.equal(count(".map.map-collapsed"), 1);
+      assert.ok(!exists(".topic-map-expanded"));
 
       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.ok(!exists(".map.map-collapsed"));
+      assert.equal(count(".topic-map .d-icon-chevron-up"), 1);
+      assert.equal(count(".topic-map-expanded"), 1);
       assert.equal(
-        queryAll(".topic-map-expanded .topic-link").length,
+        count(".topic-map-expanded .topic-link"),
         5,
         "it limits the links displayed"
       );
 
       await click(".link-summary button");
       assert.equal(
-        queryAll(".topic-map-expanded .topic-link").length,
+        count(".topic-map-expanded .topic-link"),
         6,
         "all links now shown"
       );
@@ -956,7 +886,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("args", { showTopicMap: true });
     },
     test(assert) {
-      assert.equal(queryAll(".toggle-summary").length, 0);
+      assert.ok(!exists(".toggle-summary"));
     },
   });
 
@@ -967,7 +897,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       this.set("showSummary", () => (this.summaryToggled = true));
     },
     async test(assert) {
-      assert.equal(queryAll(".toggle-summary").length, 1);
+      assert.equal(count(".toggle-summary"), 1);
 
       await click(".toggle-summary button");
       assert.ok(this.summaryToggled);
@@ -985,8 +915,8 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
       });
     },
     test(assert) {
-      assert.equal(queryAll(".private-message-map").length, 1);
-      assert.equal(queryAll(".private-message-map .user").length, 1);
+      assert.equal(count(".private-message-map"), 1);
+      assert.equal(count(".private-message-map .user"), 1);
     },
   });
 
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 b4f685b2e64..b46077a3d33 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
@@ -3,6 +3,7 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
@@ -23,9 +24,9 @@ discourseModule(
         });
       },
       test(assert) {
-        assert.ok(queryAll(".names").length);
-        assert.ok(queryAll("span.username").length);
-        assert.ok(queryAll('a[data-user-card="eviltrout"]').length);
+        assert.ok(exists(".names"));
+        assert.ok(exists("span.username"));
+        assert.ok(exists('a[data-user-card="eviltrout"]'));
         assert.equal(queryAll(".username a").text(), "eviltrout");
         assert.equal(queryAll(".full-name a").text(), "Robin Ward");
         assert.equal(queryAll(".user-title").text(), "Trout Master");
@@ -46,12 +47,12 @@ discourseModule(
         });
       },
       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);
+        assert.ok(exists("span.staff"));
+        assert.ok(exists("span.admin"));
+        assert.ok(exists("span.moderator"));
+        assert.ok(exists(".d-icon-shield-alt"));
+        assert.ok(exists("span.new-user"));
+        assert.ok(exists("span.fish"));
       },
     });
 
@@ -62,7 +63,7 @@ discourseModule(
         this.set("args", { username: "eviltrout", name: "Robin Ward" });
       },
       test(assert) {
-        assert.equal(queryAll(".full-name").length, 0);
+        assert.ok(!exists(".full-name"));
       },
     });
 
@@ -74,7 +75,7 @@ discourseModule(
         this.set("args", { username: "eviltrout", name: "evil-trout" });
       },
       test(assert) {
-        assert.equal(queryAll(".second").length, 0);
+        assert.ok(!exists(".second"));
       },
     });
   }
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 0c3f8985565..69a9f9525e2 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,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, query } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
 const CONTENT_DIV_SELECTOR = "li > a > div";
@@ -22,7 +19,7 @@ discourseModule(
       },
 
       test(assert) {
-        const contentDiv = queryAll(CONTENT_DIV_SELECTOR)[0];
+        const contentDiv = query(CONTENT_DIV_SELECTOR);
         assert.equal(contentDiv.innerText, "<b>bold</b>");
       },
     });
@@ -35,7 +32,7 @@ discourseModule(
       },
 
       test(assert) {
-        const contentDiv = queryAll(CONTENT_DIV_SELECTOR)[0];
+        const contentDiv = query(CONTENT_DIV_SELECTOR);
         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 ac8dbcdaefa..3488879b19f 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
@@ -2,8 +2,9 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
-  queryAll,
+  exists,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 
@@ -23,9 +24,9 @@ discourseModule(
         });
       },
       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 unknown user");
+        assert.equal(count('[data-user-card="eviltrout"]'), 1);
+        assert.ok(!exists('[data-user-card="someone"]'));
+        assert.ok(exists(".unknown"), "includes unknown user");
       },
     });
   }
diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/software-update-prompt-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/software-update-prompt-test.js
index dec9287d47f..881f94aa95a 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/software-update-prompt-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/software-update-prompt-test.js
@@ -2,9 +2,10 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
+  exists,
   publishToMessageBus,
-  queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import hbs from "htmlbars-inline-precompile";
 import { later } from "@ember/runloop";
@@ -21,7 +22,7 @@ discourseModule(
 
         test(assert) {
           assert.ok(
-            queryAll("div.software-update-prompt").length === 0,
+            !exists("div.software-update-prompt"),
             "it does not have the class to show the prompt"
           );
 
@@ -29,9 +30,9 @@ discourseModule(
 
           const done = assert.async();
           later(() => {
-            assert.ok(
-              queryAll("div.software-update-prompt.require-software-refresh")
-                .length === 1,
+            assert.equal(
+              count("div.software-update-prompt.require-software-refresh"),
+              1,
               "it does have the class to show the prompt"
             );
             done();
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 683a074c062..08eaccb51fa 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,10 +1,7 @@
 import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
-import {
-  discourseModule,
-  queryAll,
-} from "discourse/tests/helpers/qunit-helpers";
+import { discourseModule, exists } from "discourse/tests/helpers/qunit-helpers";
 import TopicStatusIcons from "discourse/helpers/topic-status-icons";
 import hbs from "htmlbars-inline-precompile";
 
@@ -22,7 +19,7 @@ discourseModule(
         });
       },
       test(assert) {
-        assert.ok(queryAll(".topic-status .d-icon-lock").length);
+        assert.ok(exists(".topic-status .d-icon-lock"));
       },
     });
 
@@ -42,7 +39,7 @@ discourseModule(
         });
       },
       test(assert) {
-        assert.ok(queryAll(".topic-status .d-icon-far-check-square").length);
+        assert.ok(exists(".topic-status .d-icon-far-check-square"));
       },
     });
   }
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 6654fa504dd..624c6837197 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
@@ -3,6 +3,8 @@ import componentTest, {
 } from "discourse/tests/helpers/component-test";
 import {
   discourseModule,
+  exists,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import DiscourseURL from "discourse/lib/url";
@@ -20,12 +22,12 @@ discourseModule(
       template: hbs`{{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);
+        assert.ok(exists(".user-menu"));
+        assert.ok(exists(".user-preferences-link"));
+        assert.ok(exists(".user-notifications-link"));
+        assert.ok(exists(".user-bookmarks-link"));
+        assert.ok(exists(".quick-access-panel"));
+        assert.ok(exists(".notifications-dismiss"));
       },
     });
 
@@ -98,7 +100,7 @@ discourseModule(
       async test(assert) {
         await click(".user-preferences-link");
 
-        assert.ok(queryAll(".logout").length);
+        assert.ok(exists(".logout"));
 
         await click(".logout button");
         assert.ok(this.loggedOut);
@@ -112,7 +114,7 @@ discourseModule(
       },
 
       test(assert) {
-        assert.ok(!queryAll(".user-pms-link").length);
+        assert.ok(!exists(".user-pms-link"));
       },
     });
 
@@ -127,7 +129,7 @@ discourseModule(
         assert.ok(userPmsLink);
         await click(".user-pms-link");
 
-        const message = queryAll(".quick-access-panel li a")[0];
+        const message = query(".quick-access-panel li a");
         assert.ok(message);
 
         assert.ok(
@@ -158,7 +160,7 @@ discourseModule(
       async test(assert) {
         await click(".user-bookmarks-link");
 
-        const bookmark = queryAll(".quick-access-panel li a")[0];
+        const bookmark = query(".quick-access-panel li a");
         assert.ok(bookmark);
 
         assert.ok(bookmark.href.includes("/t/yelling-topic-title/119"));
@@ -195,7 +197,7 @@ discourseModule(
 
       async test(assert) {
         await click(".user-preferences-link");
-        assert.ok(queryAll(".enable-anonymous").length);
+        assert.ok(exists(".enable-anonymous"));
 
         await click(".enable-anonymous");
         assert.ok(this.anonymous);
@@ -211,7 +213,7 @@ discourseModule(
 
       async test(assert) {
         await click(".user-preferences-link");
-        assert.ok(!queryAll(".enable-anonymous").length);
+        assert.ok(!exists(".enable-anonymous"));
       },
     });
 
@@ -229,7 +231,7 @@ discourseModule(
 
       async test(assert) {
         await click(".user-preferences-link");
-        assert.ok(queryAll(".disable-anonymous").length);
+        assert.ok(exists(".disable-anonymous"));
 
         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 49e0060dcfa..2b298090b49 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
@@ -4,6 +4,7 @@ import componentTest, {
 import {
   discourseModule,
   exists,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import I18n from "I18n";
@@ -27,7 +28,7 @@ async function clickRowById(id) {
 }
 
 function rowById(id) {
-  return queryAll(`#my-dropdown .widget-dropdown-item.item-${id}`)[0];
+  return query(`#my-dropdown .widget-dropdown-item.item-${id}`);
 }
 
 async function toggle() {
@@ -41,11 +42,11 @@ function headerLabel() {
 }
 
 function header() {
-  return queryAll("#my-dropdown .widget-dropdown-header")[0];
+  return query("#my-dropdown .widget-dropdown-header");
 }
 
 function body() {
-  return queryAll("#my-dropdown .widget-dropdown-body")[0];
+  return query("#my-dropdown .widget-dropdown-body");
 }
 
 const TEMPLATE = hbs`
@@ -150,10 +151,7 @@ discourseModule(
       beforeEach() {
         this.setProperties(DEFAULT_CONTENT);
 
-        this.set(
-          "onChange",
-          (item) => (queryAll("#test")[0].innerText = item.id)
-        );
+        this.set("onChange", (item) => (query("#test").innerText = item.id));
       },
 
       async test(assert) {
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 aa1a27b0164..6d184679175 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/widget-test.js
@@ -2,7 +2,10 @@ import componentTest, {
   setupRenderingTest,
 } from "discourse/tests/helpers/component-test";
 import {
+  count,
   discourseModule,
+  exists,
+  query,
   queryAll,
 } from "discourse/tests/helpers/qunit-helpers";
 import I18n from "I18n";
@@ -97,10 +100,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(
-        queryAll(".test.static.cool-class").length,
-        "it has all the classes"
-      );
+      assert.ok(exists(".test.static.cool-class"), "it has all the classes");
     },
   });
 
@@ -120,8 +120,8 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll('.test[data-evil="trout"]').length);
-      assert.ok(queryAll('.test[aria-label="accessibility"]').length);
+      assert.ok(exists('.test[data-evil="trout"]'));
+      assert.ok(exists('.test[aria-label="accessibility"]'));
     },
   });
 
@@ -139,7 +139,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll("#test-1234").length);
+      assert.ok(exists("#test-1234"));
     },
   });
 
@@ -163,10 +163,10 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     async test(assert) {
-      assert.ok(queryAll("button.test").length, "it renders the button");
+      assert.ok(exists("button.test"), "it renders the button");
       assert.equal(queryAll("button.test").text(), "0 clicks");
 
-      await click(queryAll("button")[0]);
+      await click(query("button"));
       assert.equal(queryAll("button.test").text(), "1 clicks");
     },
   });
@@ -200,7 +200,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     async test(assert) {
       assert.equal(queryAll("button.test").text().trim(), "No name");
 
-      await click(queryAll("button")[0]);
+      await click(query("button"));
       assert.equal(queryAll("button.test").text().trim(), "Robin");
     },
   });
@@ -218,8 +218,8 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll(".container").length, "renders container");
-      assert.ok(queryAll(".container .embedded").length, "renders attached");
+      assert.ok(exists(".container"), "renders container");
+      assert.ok(exists(".container .embedded"), "renders attached");
     },
   });
 
@@ -236,8 +236,8 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll(".container").length, "renders container");
-      assert.ok(queryAll(".container .embedded").length, "renders attached");
+      assert.ok(exists(".container"), "renders container");
+      assert.ok(exists(".container .embedded"), "renders attached");
     },
   });
 
@@ -257,7 +257,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll(".container").length, "renders container");
+      assert.ok(exists(".container"), "renders container");
       assert.equal(queryAll(".container .value").text(), "hello world");
     },
   });
@@ -296,7 +296,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.equal(queryAll(".d-icon-arrow-down").length, 1);
+      assert.equal(count(".d-icon-arrow-down"), 1);
     },
   });
 
@@ -355,7 +355,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.equal(queryAll("ul li").length, 3);
+      assert.equal(count("ul li"), 3);
       assert.equal(queryAll("ul li:nth-of-type(1)").text(), "one");
     },
   });
@@ -381,7 +381,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
     },
 
     test(assert) {
-      assert.ok(queryAll(".decorate").length);
+      assert.ok(exists(".decorate"));
       assert.equal(queryAll(".decorate b").text(), "before");
       assert.equal(queryAll(".decorate i").text(), "after");
     },
@@ -456,7 +456,7 @@ discourseModule("Integration | Component | Widget | base", function (hooks) {
 
     test(assert) {
       assert.ok(
-        queryAll("section.override").length,
+        exists("section.override"),
         "renders container with overrided tagName"
       );
     },
diff --git a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6 b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6
index 34f0437f62e..25c310dac34 100644
--- a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6
+++ b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  query,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import I18n from "I18n";
 import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer";
 import selectKit from "discourse/tests/helpers/select-kit-helper";
@@ -26,7 +30,7 @@ acceptance("Details Button", function (needs) {
 
     await fillIn(".d-editor-input", "This is my title");
 
-    const textarea = queryAll(".d-editor-input")[0];
+    const textarea = query(".d-editor-input");
     textarea.selectionStart = 0;
     textarea.selectionEnd = textarea.value.length;
 
@@ -115,7 +119,7 @@ acceptance("Details Button", function (needs) {
     await click("#create-topic");
     await fillIn(".d-editor-input", multilineInput);
 
-    const textarea = queryAll(".d-editor-input")[0];
+    const textarea = query(".d-editor-input");
     textarea.selectionStart = 0;
     textarea.selectionEnd = textarea.value.length;
 
diff --git a/plugins/poll/test/javascripts/acceptance/poll-breakdown-test.js.es6 b/plugins/poll/test/javascripts/acceptance/poll-breakdown-test.js.es6
index 28bfd6d4500..38aba91743c 100644
--- a/plugins/poll/test/javascripts/acceptance/poll-breakdown-test.js.es6
+++ b/plugins/poll/test/javascripts/acceptance/poll-breakdown-test.js.es6
@@ -1,4 +1,9 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  count,
+  query,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer";
 
 acceptance("Poll breakdown", function (needs) {
@@ -69,19 +74,19 @@ acceptance("Poll breakdown", function (needs) {
     await click(".poll-show-breakdown");
 
     assert.equal(
-      queryAll(".poll-breakdown-total-votes")[0].textContent.trim(),
+      query(".poll-breakdown-total-votes").textContent.trim(),
       "2 votes",
       "display the correct total vote count"
     );
 
     assert.equal(
-      queryAll(".poll-breakdown-chart-container").length,
+      count(".poll-breakdown-chart-container"),
       2,
       "renders a chart for each of the groups in group_results response"
     );
 
     assert.ok(
-      queryAll(".poll-breakdown-chart-container > canvas")[0].$chartjs,
+      query(".poll-breakdown-chart-container > canvas").$chartjs,
       "$chartjs is defined on the pie charts"
     );
   });
@@ -91,7 +96,7 @@ acceptance("Poll breakdown", function (needs) {
     await click(".poll-show-breakdown");
 
     assert.equal(
-      queryAll(".poll-breakdown-option-count")[0].textContent.trim(),
+      query(".poll-breakdown-option-count").textContent.trim(),
       "40.0%",
       "displays the correct vote percentage"
     );
@@ -99,7 +104,7 @@ acceptance("Poll breakdown", function (needs) {
     await click(".modal-tabs .count");
 
     assert.equal(
-      queryAll(".poll-breakdown-option-count")[0].textContent.trim(),
+      query(".poll-breakdown-option-count").textContent.trim(),
       "2",
       "displays the correct vote count"
     );
@@ -107,8 +112,8 @@ acceptance("Poll breakdown", function (needs) {
     await click(".modal-tabs .percentage");
 
     assert.equal(
-      queryAll(".poll-breakdown-option-count:last")[0].textContent.trim(),
-      "20.0%",
+      query(".poll-breakdown-option-count").textContent.trim(),
+      "40.0%",
       "displays the percentage again"
     );
   });
diff --git a/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js.es6 b/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js.es6
index 721f764a1d9..994df7fdb42 100644
--- a/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js.es6
+++ b/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js.es6
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  query,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 
 acceptance("Rendering polls with pie charts", function (needs) {
   needs.user();
@@ -10,10 +14,10 @@ acceptance("Rendering polls with pie charts", function (needs) {
   test("Displays the pie chart", async function (assert) {
     await visit("/t/-/topic_with_pie_chart_poll");
 
-    const poll = queryAll(".poll")[0];
+    const poll = query(".poll");
 
     assert.equal(
-      queryAll(".info-number", poll)[0].innerHTML,
+      query(".info-number", poll).innerHTML,
       "2",
       "it should display the right number of voters"
     );
diff --git a/plugins/poll/test/javascripts/acceptance/poll-quote-test.js.es6 b/plugins/poll/test/javascripts/acceptance/poll-quote-test.js.es6
index 846f97c4a14..d88ee700e6d 100644
--- a/plugins/poll/test/javascripts/acceptance/poll-quote-test.js.es6
+++ b/plugins/poll/test/javascripts/acceptance/poll-quote-test.js.es6
@@ -1,4 +1,4 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
 import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer";
 
 acceptance("Poll quote", function (needs) {
@@ -427,7 +427,7 @@ acceptance("Poll quote", function (needs) {
   test("renders and extends", async function (assert) {
     await visit("/t/-/topic_with_two_quoted_polls");
     await click(".quote-controls");
-    assert.equal(queryAll(".poll").length, 2, "polls are rendered");
-    assert.equal(queryAll(".poll-buttons").length, 2, "polls are extended");
+    assert.equal(count(".poll"), 2, "polls are rendered");
+    assert.equal(count(".poll-buttons"), 2, "polls are extended");
   });
 });
diff --git a/plugins/poll/test/javascripts/widgets/discourse-poll-test.js.es6 b/plugins/poll/test/javascripts/widgets/discourse-poll-test.js.es6
index 6be48becf7f..fc1efc0e674 100644
--- a/plugins/poll/test/javascripts/widgets/discourse-poll-test.js.es6
+++ b/plugins/poll/test/javascripts/widgets/discourse-poll-test.js.es6
@@ -4,7 +4,7 @@ import {
 } from "discourse/tests/helpers/widget-test";
 import EmberObject from "@ember/object";
 import I18n from "I18n";
-import { queryAll } from "discourse/tests/helpers/qunit-helpers";
+import { count, exists, queryAll } from "discourse/tests/helpers/qunit-helpers";
 
 let requests = 0;
 
@@ -100,7 +100,7 @@ widgetTest("can vote", {
 
     await click("li[data-poll-option-id='1f972d1df351de3ce35a787c89faad29']");
     assert.equal(requests, 1);
-    assert.equal(queryAll(".chosen").length, 1);
+    assert.equal(count(".chosen"), 1);
     assert.equal(queryAll(".chosen").text(), "100%yes");
     assert.equal(queryAll(".toggle-results").text(), "Show vote");
 
@@ -152,6 +152,6 @@ widgetTest("cannot vote if not member of the right group", {
       I18n.t("poll.results.groups.title", { groups: "foo" })
     );
     assert.equal(requests, 0);
-    assert.equal(queryAll(".chosen").length, 0);
+    assert.ok(!exists(".chosen"));
   },
 });