DEV: Use async functions in tests (#9087)

This change both improves readability and fixes potential race-condition issues where promises were nested instead of being chained.

Also includes:
* Use arrow functions and Promise shorthands
* Remove the obsolete `asyncTestDiscourse` helper
This commit is contained in:
Jarek Radosz 2020-03-02 21:20:19 +01:00 committed by GitHub
parent 537f87562e
commit 4da357e378
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 241 additions and 318 deletions

View File

@ -1,6 +1,6 @@
moduleFor("controller:history");
QUnit.test("displayEdit", function(assert) {
QUnit.test("displayEdit", async function(assert) {
const HistoryController = this.subject();
HistoryController.setProperties({
@ -82,8 +82,8 @@ QUnit.test("displayEdit", function(assert) {
}
});
HistoryController.bodyDiffChanged().then(() => {
const output = HistoryController.get("bodyDiff");
assert.equal(output, expectedOutput, "it keeps safe HTML");
});
await HistoryController.bodyDiffChanged();
const output = HistoryController.get("bodyDiff");
assert.equal(output, expectedOutput, "it keeps safe HTML");
});

View File

@ -1,5 +1,4 @@
import { isEmpty } from "@ember/utils";
import { run } from "@ember/runloop";
import { later } from "@ember/runloop";
/* global QUnit, resetSite */
@ -156,16 +155,6 @@ export function controllerFor(controller, model) {
return controller;
}
export function asyncTestDiscourse(text, func) {
QUnit.test(text, function(assert) {
const done = assert.async();
run(() => {
func.call(this, assert);
done();
});
});
}
export function fixture(selector) {
if (selector) {
return $("#qunit-fixture").find(selector);

View File

@ -12,11 +12,10 @@ QUnit.skip(
const src = "/javascripts/ace/ace.js";
await loadScript(src).then(() => {
assert.ok(
typeof window.ace !== "undefined",
"callbacks should only be executed after the script has fully loaded"
);
});
await loadScript(src);
assert.ok(
typeof window.ace !== "undefined",
"callbacks should only be executed after the script has fully loaded"
);
}
);

View File

@ -1,5 +1,4 @@
import PreloadStore from "preload-store";
import { asyncTestDiscourse } from "helpers/qunit-helpers";
import { Promise } from "rsvp";
QUnit.module("preload-store", {
@ -22,81 +21,45 @@ QUnit.test("remove", assert => {
assert.blank(PreloadStore.get("bane"), "removes the value if the key exists");
});
asyncTestDiscourse(
QUnit.test(
"getAndRemove returns a promise that resolves to null",
function(assert) {
assert.expect(1);
const done = assert.async();
PreloadStore.getAndRemove("joker").then(function(result) {
assert.blank(result);
done();
});
async assert => {
assert.blank(await PreloadStore.getAndRemove("joker"));
}
);
asyncTestDiscourse(
QUnit.test(
"getAndRemove returns a promise that resolves to the result of the finder",
function(assert) {
assert.expect(1);
async assert => {
const finder = () => "batdance";
const result = await PreloadStore.getAndRemove("joker", finder);
const done = assert.async();
const finder = function() {
return "batdance";
};
PreloadStore.getAndRemove("joker", finder).then(function(result) {
assert.equal(result, "batdance");
done();
});
assert.equal(result, "batdance");
}
);
asyncTestDiscourse(
QUnit.test(
"getAndRemove returns a promise that resolves to the result of the finder's promise",
function(assert) {
assert.expect(1);
async assert => {
const finder = () => Promise.resolve("hahahah");
const result = await PreloadStore.getAndRemove("joker", finder);
const finder = function() {
return new Promise(function(resolve) {
resolve("hahahah");
});
};
const done = assert.async();
PreloadStore.getAndRemove("joker", finder).then(function(result) {
assert.equal(result, "hahahah");
done();
});
assert.equal(result, "hahahah");
}
);
asyncTestDiscourse(
QUnit.test(
"returns a promise that rejects with the result of the finder's rejected promise",
function(assert) {
assert.expect(1);
async assert => {
const finder = () => Promise.reject("error");
const finder = function() {
return new Promise(function(resolve, reject) {
reject("error");
});
};
const done = assert.async();
PreloadStore.getAndRemove("joker", finder).then(null, function(result) {
await PreloadStore.getAndRemove("joker", finder).catch(result => {
assert.equal(result, "error");
done();
});
}
);
asyncTestDiscourse("returns a promise that resolves to 'evil'", function(
assert
) {
assert.expect(1);
const done = assert.async();
PreloadStore.getAndRemove("bane").then(function(result) {
assert.equal(result, "evil");
done();
});
QUnit.test("returns a promise that resolves to 'evil'", async assert => {
const result = await PreloadStore.getAndRemove("bane");
assert.equal(result, "evil");
});

View File

@ -446,7 +446,7 @@ QUnit.test("storePost", assert => {
assert.equal(stored, postWithoutId, "it returns the same post back");
});
QUnit.test("identity map", assert => {
QUnit.test("identity map", async assert => {
const postStream = buildStream(1234);
const store = postStream.store;
@ -465,34 +465,29 @@ QUnit.test("identity map", assert => {
assert.blank(postStream.findLoadedPost(4), "it can't find uncached posts");
// Find posts by ids uses the identity map
return postStream.findPostsByIds([1, 2, 3]).then(result => {
assert.equal(result.length, 3);
assert.equal(result.objectAt(0), p1);
assert.equal(result.objectAt(1).get("post_number"), 2);
assert.equal(result.objectAt(2), p3);
});
const result = await postStream.findPostsByIds([1, 2, 3]);
assert.equal(result.length, 3);
assert.equal(result.objectAt(0), p1);
assert.equal(result.objectAt(1).get("post_number"), 2);
assert.equal(result.objectAt(2), p3);
});
QUnit.test("loadIntoIdentityMap with no data", assert => {
return buildStream(1234)
.loadIntoIdentityMap([])
.then(result => {
assert.equal(result.length, 0, "requesting no posts produces no posts");
});
QUnit.test("loadIntoIdentityMap with no data", async assert => {
const result = await buildStream(1234).loadIntoIdentityMap([]);
assert.equal(result.length, 0, "requesting no posts produces no posts");
});
QUnit.test("loadIntoIdentityMap with post ids", assert => {
QUnit.test("loadIntoIdentityMap with post ids", async assert => {
const postStream = buildStream(1234);
await postStream.loadIntoIdentityMap([10]);
return postStream.loadIntoIdentityMap([10]).then(function() {
assert.present(
postStream.findLoadedPost(10),
"it adds the returned post to the store"
);
});
assert.present(
postStream.findLoadedPost(10),
"it adds the returned post to the store"
);
});
QUnit.test("appendMore for megatopic", assert => {
QUnit.test("appendMore for megatopic", async assert => {
const postStream = buildStream(1234);
const store = createStore();
const post = store.createRecord("post", { id: 1, post_number: 1 });
@ -502,21 +497,20 @@ QUnit.test("appendMore for megatopic", assert => {
posts: [post]
});
return postStream.appendMore().then(() => {
assert.present(
postStream.findLoadedPost(2),
"it adds the returned post to the store"
);
await postStream.appendMore();
assert.present(
postStream.findLoadedPost(2),
"it adds the returned post to the store"
);
assert.equal(
postStream.get("posts").length,
6,
"it adds the right posts into the stream"
);
});
assert.equal(
postStream.get("posts").length,
6,
"it adds the right posts into the stream"
);
});
QUnit.test("prependMore for megatopic", assert => {
QUnit.test("prependMore for megatopic", async assert => {
const postStream = buildStream(1234);
const store = createStore();
const post = store.createRecord("post", { id: 6, post_number: 6 });
@ -526,18 +520,17 @@ QUnit.test("prependMore for megatopic", assert => {
posts: [post]
});
return postStream.prependMore().then(() => {
assert.present(
postStream.findLoadedPost(5),
"it adds the returned post to the store"
);
await postStream.prependMore();
assert.present(
postStream.findLoadedPost(5),
"it adds the returned post to the store"
);
assert.equal(
postStream.get("posts").length,
6,
"it adds the right posts into the stream"
);
});
assert.equal(
postStream.get("posts").length,
6,
"it adds the right posts into the stream"
);
});
QUnit.test("staging and undoing a new post", assert => {
@ -865,7 +858,7 @@ QUnit.test("triggerNewPostInStream for ignored posts", async assert => {
);
});
QUnit.test("postsWithPlaceholders", assert => {
QUnit.test("postsWithPlaceholders", async assert => {
const postStream = buildStream(4964, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const postsWithPlaceholders = postStream.get("postsWithPlaceholders");
const store = postStream.store;
@ -903,16 +896,15 @@ QUnit.test("postsWithPlaceholders", assert => {
assert.ok(postsWithPlaceholders.objectAt(3) !== p4);
assert.ok(testProxy.objectAt(3) !== p4);
return promise.then(() => {
assert.equal(postsWithPlaceholders.objectAt(3), p4);
assert.equal(
postsWithPlaceholders.get("length"),
8,
"have a larger placeholder window when loaded"
);
assert.equal(testProxy.get("length"), 8);
assert.equal(testProxy.objectAt(3), p4);
});
await promise;
assert.equal(postsWithPlaceholders.objectAt(3), p4);
assert.equal(
postsWithPlaceholders.get("length"),
8,
"have a larger placeholder window when loaded"
);
assert.equal(testProxy.get("length"), 8);
assert.equal(testProxy.objectAt(3), p4);
});
QUnit.test("filteredPostsCount", assert => {

View File

@ -80,20 +80,20 @@ QUnit.test("destroy by staff", assert => {
);
});
QUnit.test("destroy by non-staff", assert => {
var originalCooked = "this is the original cooked value",
user = User.create({ username: "evil trout" }),
post = buildPost({ user: user, cooked: originalCooked });
QUnit.test("destroy by non-staff", async assert => {
const originalCooked = "this is the original cooked value";
const user = User.create({ username: "evil trout" });
const post = buildPost({ user: user, cooked: originalCooked });
return post.destroy(user).then(() => {
assert.ok(
!post.get("can_delete"),
"the post can't be deleted again in this session"
);
assert.ok(
post.get("cooked") !== originalCooked,
"the cooked content changed"
);
assert.equal(post.get("version"), 2, "the version number increased");
});
await post.destroy(user);
assert.ok(
!post.get("can_delete"),
"the post can't be deleted again in this session"
);
assert.ok(
post.get("cooked") !== originalCooked,
"the cooked content changed"
);
assert.equal(post.get("version"), 2, "the version number increased");
});

View File

@ -18,43 +18,42 @@ QUnit.test("munging", assert => {
assert.equal(g.get("inverse"), 0.6, "it runs `munge` on `create`");
});
QUnit.test("update", assert => {
QUnit.test("update", async assert => {
const store = createStore();
return store.find("widget", 123).then(function(widget) {
assert.equal(widget.get("name"), "Trout Lure");
assert.ok(!widget.get("isSaving"), "it is not saving");
const widget = await store.find("widget", 123);
assert.equal(widget.get("name"), "Trout Lure");
assert.ok(!widget.get("isSaving"), "it is not saving");
const promise = widget.update({ name: "new name" });
assert.ok(widget.get("isSaving"), "it is saving");
const promise = widget.update({ name: "new name" });
assert.ok(widget.get("isSaving"), "it is saving");
promise.then(function(result) {
assert.ok(!widget.get("isSaving"), "it is no longer saving");
assert.equal(widget.get("name"), "new name");
const result = await promise;
assert.ok(!widget.get("isSaving"), "it is no longer saving");
assert.equal(widget.get("name"), "new name");
assert.ok(result.target, "it has a reference to the record");
assert.equal(result.target.name, widget.get("name"));
});
});
assert.ok(result.target, "it has a reference to the record");
assert.equal(result.target.name, widget.get("name"));
});
QUnit.test("updating simultaneously", assert => {
QUnit.test("updating simultaneously", async assert => {
assert.expect(2);
const store = createStore();
return store.find("widget", 123).then(function(widget) {
const firstPromise = widget.update({ name: "new name" });
const secondPromise = widget.update({ name: "new name" });
firstPromise.then(function() {
assert.ok(true, "the first promise succeeeds");
});
const widget = await store.find("widget", 123);
secondPromise.catch(function() {
assert.ok(true, "the second promise fails");
});
const firstPromise = widget.update({ name: "new name" });
const secondPromise = widget.update({ name: "new name" });
firstPromise.then(function() {
assert.ok(true, "the first promise succeeeds");
});
secondPromise.catch(function() {
assert.ok(true, "the second promise fails");
});
});
QUnit.test("save new", assert => {
QUnit.test("save new", async assert => {
const store = createStore();
const widget = store.createRecord("widget");
@ -65,16 +64,15 @@ QUnit.test("save new", assert => {
const promise = widget.save({ name: "Evil Widget" });
assert.ok(widget.get("isSaving"), "it is not saving");
return promise.then(function(result) {
assert.ok(!widget.get("isSaving"), "it is no longer saving");
assert.ok(widget.get("id"), "it has an id");
assert.ok(widget.get("name"), "Evil Widget");
assert.ok(widget.get("isCreated"), "it is created");
assert.ok(!widget.get("isNew"), "it is no longer new");
const result = await promise;
assert.ok(!widget.get("isSaving"), "it is no longer saving");
assert.ok(widget.get("id"), "it has an id");
assert.ok(widget.get("name"), "Evil Widget");
assert.ok(widget.get("isCreated"), "it is created");
assert.ok(!widget.get("isNew"), "it is no longer new");
assert.ok(result.target, "it has a reference to the record");
assert.equal(result.target.name, widget.get("name"));
});
assert.ok(result.target, "it has a reference to the record");
assert.equal(result.target.name, widget.get("name"));
});
QUnit.test("creating simultaneously", assert => {

View File

@ -4,50 +4,46 @@ import ResultSet from "discourse/models/result-set";
import createStore from "helpers/create-store";
QUnit.test("defaults", assert => {
const rs = ResultSet.create({ content: [] });
assert.equal(rs.get("length"), 0);
assert.equal(rs.get("totalRows"), 0);
assert.ok(!rs.get("loadMoreUrl"));
assert.ok(!rs.get("loading"));
assert.ok(!rs.get("loadingMore"));
assert.ok(!rs.get("refreshing"));
const resultSet = ResultSet.create({ content: [] });
assert.equal(resultSet.get("length"), 0);
assert.equal(resultSet.get("totalRows"), 0);
assert.ok(!resultSet.get("loadMoreUrl"));
assert.ok(!resultSet.get("loading"));
assert.ok(!resultSet.get("loadingMore"));
assert.ok(!resultSet.get("refreshing"));
});
QUnit.test("pagination support", assert => {
QUnit.test("pagination support", async assert => {
const store = createStore();
return store.findAll("widget").then(function(rs) {
assert.equal(rs.get("length"), 2);
assert.equal(rs.get("totalRows"), 4);
assert.ok(rs.get("loadMoreUrl"), "has a url to load more");
assert.ok(!rs.get("loadingMore"), "it is not loading more");
assert.ok(rs.get("canLoadMore"));
const resultSet = await store.findAll("widget");
assert.equal(resultSet.get("length"), 2);
assert.equal(resultSet.get("totalRows"), 4);
assert.ok(resultSet.get("loadMoreUrl"), "has a url to load more");
assert.ok(!resultSet.get("loadingMore"), "it is not loading more");
assert.ok(resultSet.get("canLoadMore"));
const promise = rs.loadMore();
const promise = resultSet.loadMore();
assert.ok(resultSet.get("loadingMore"), "it is loading more");
assert.ok(rs.get("loadingMore"), "it is loading more");
promise.then(function() {
assert.ok(!rs.get("loadingMore"), "it finished loading more");
assert.equal(rs.get("length"), 4);
assert.ok(!rs.get("loadMoreUrl"));
assert.ok(!rs.get("canLoadMore"));
});
});
await promise;
assert.ok(!resultSet.get("loadingMore"), "it finished loading more");
assert.equal(resultSet.get("length"), 4);
assert.ok(!resultSet.get("loadMoreUrl"));
assert.ok(!resultSet.get("canLoadMore"));
});
QUnit.test("refresh support", assert => {
QUnit.test("refresh support", async assert => {
const store = createStore();
return store.findAll("widget").then(function(rs) {
assert.equal(
rs.get("refreshUrl"),
"/widgets?refresh=true",
"it has the refresh url"
);
const resultSet = await store.findAll("widget");
assert.equal(
resultSet.get("refreshUrl"),
"/widgets?refresh=true",
"it has the refresh url"
);
const promise = rs.refresh();
const promise = resultSet.refresh();
assert.ok(resultSet.get("refreshing"), "it is refreshing");
assert.ok(rs.get("refreshing"), "it is refreshing");
promise.then(function() {
assert.ok(!rs.get("refreshing"), "it is finished refreshing");
});
});
await promise;
assert.ok(!resultSet.get("refreshing"), "it is finished refreshing");
});

View File

@ -50,77 +50,68 @@ QUnit.test(
}
);
QUnit.test("find", assert => {
QUnit.test("find", async assert => {
const store = createStore();
return store.find("widget", 123).then(function(w) {
assert.equal(w.get("name"), "Trout Lure");
assert.equal(w.get("id"), 123);
assert.ok(!w.get("isNew"), "found records are not new");
assert.equal(w.get("extras.hello"), "world", "extra attributes are set");
const widget = await store.find("widget", 123);
assert.equal(widget.get("name"), "Trout Lure");
assert.equal(widget.get("id"), 123);
assert.ok(!widget.get("isNew"), "found records are not new");
assert.equal(widget.get("extras.hello"), "world", "extra attributes are set");
// A second find by id returns the same object
store.find("widget", 123).then(function(w2) {
assert.equal(w, w2);
assert.equal(w.get("extras.hello"), "world", "extra attributes are set");
});
});
// A second find by id returns the same object
const widget2 = await store.find("widget", 123);
assert.equal(widget, widget2);
assert.equal(widget.get("extras.hello"), "world", "extra attributes are set");
});
QUnit.test("find with object id", assert => {
QUnit.test("find with object id", async assert => {
const store = createStore();
return store.find("widget", { id: 123 }).then(function(w) {
assert.equal(w.get("firstObject.name"), "Trout Lure");
});
const widget = await store.find("widget", { id: 123 });
assert.equal(widget.get("firstObject.name"), "Trout Lure");
});
QUnit.test("find with query param", assert => {
QUnit.test("find with query param", async assert => {
const store = createStore();
return store.find("widget", { name: "Trout Lure" }).then(function(w) {
assert.equal(w.get("firstObject.id"), 123);
});
const widget = await store.find("widget", { name: "Trout Lure" });
assert.equal(widget.get("firstObject.id"), 123);
});
QUnit.test("findStale with no stale results", assert => {
QUnit.test("findStale with no stale results", async assert => {
const store = createStore();
const stale = store.findStale("widget", { name: "Trout Lure" });
assert.ok(!stale.hasResults, "there are no stale results");
assert.ok(!stale.results, "results are present");
return stale.refresh().then(function(w) {
assert.equal(
w.get("firstObject.id"),
123,
"a `refresh()` method provides results for stale"
);
});
const widget = await stale.refresh();
assert.equal(
widget.get("firstObject.id"),
123,
"a `refresh()` method provides results for stale"
);
});
QUnit.test("update", assert => {
QUnit.test("update", async assert => {
const store = createStore();
return store.update("widget", 123, { name: "hello" }).then(function(result) {
assert.ok(result);
});
const result = await store.update("widget", 123, { name: "hello" });
assert.ok(result);
});
QUnit.test("update with a multi world name", function(assert) {
QUnit.test("update with a multi world name", async assert => {
const store = createStore();
return store
.update("cool-thing", 123, { name: "hello" })
.then(function(result) {
assert.ok(result);
assert.equal(result.payload.name, "hello");
});
const result = await store.update("cool-thing", 123, { name: "hello" });
assert.ok(result);
assert.equal(result.payload.name, "hello");
});
QUnit.test("findAll", assert => {
QUnit.test("findAll", async assert => {
const store = createStore();
return store.findAll("widget").then(function(result) {
assert.equal(result.get("length"), 2);
const w = result.findBy("id", 124);
assert.ok(!w.get("isNew"), "found records are not new");
assert.equal(w.get("name"), "Evil Repellant");
});
const result = await store.findAll("widget");
assert.equal(result.get("length"), 2);
const widget = result.findBy("id", 124);
assert.ok(!widget.get("isNew"), "found records are not new");
assert.equal(widget.get("name"), "Evil Repellant");
});
QUnit.test("destroyRecord", function(assert) {
@ -140,59 +131,57 @@ QUnit.test("destroyRecord when new", function(assert) {
});
});
QUnit.test("find embedded", function(assert) {
QUnit.test("find embedded", async assert => {
const store = createStore();
return store.find("fruit", 1).then(function(f) {
assert.ok(f.get("farmer"), "it has the embedded object");
const fruit = await store.find("fruit", 1);
assert.ok(fruit.get("farmer"), "it has the embedded object");
const fruitCols = f.get("colors");
assert.equal(fruitCols.length, 2);
assert.equal(fruitCols[0].get("id"), 1);
assert.equal(fruitCols[1].get("id"), 2);
const fruitCols = fruit.get("colors");
assert.equal(fruitCols.length, 2);
assert.equal(fruitCols[0].get("id"), 1);
assert.equal(fruitCols[1].get("id"), 2);
assert.ok(f.get("category"), "categories are found automatically");
});
assert.ok(fruit.get("category"), "categories are found automatically");
});
QUnit.test("embedded records can be cleared", async assert => {
const store = createStore();
let f = await store.find("fruit", 4);
f.set("farmer", { dummy: "object" });
f = await store.find("fruit", 4);
assert.ok(!f.get("farmer"));
let fruit = await store.find("fruit", 4);
fruit.set("farmer", { dummy: "object" });
fruit = await store.find("fruit", 4);
assert.ok(!fruit.get("farmer"));
});
QUnit.test("meta types", function(assert) {
QUnit.test("meta types", async assert => {
const store = createStore();
return store.find("barn", 1).then(function(f) {
assert.equal(
f.get("owner.name"),
"Old MacDonald",
"it has the embedded farmer"
);
});
const barn = await store.find("barn", 1);
assert.equal(
barn.get("owner.name"),
"Old MacDonald",
"it has the embedded farmer"
);
});
QUnit.test("findAll embedded", function(assert) {
QUnit.test("findAll embedded", async assert => {
const store = createStore();
return store.findAll("fruit").then(function(fruits) {
assert.equal(fruits.objectAt(0).get("farmer.name"), "Old MacDonald");
assert.equal(
fruits.objectAt(0).get("farmer"),
fruits.objectAt(1).get("farmer"),
"points at the same object"
);
assert.equal(
fruits.get("extras.hello"),
"world",
"it can supply extra information"
);
const fruits = await store.findAll("fruit");
assert.equal(fruits.objectAt(0).get("farmer.name"), "Old MacDonald");
assert.equal(
fruits.objectAt(0).get("farmer"),
fruits.objectAt(1).get("farmer"),
"points at the same object"
);
assert.equal(
fruits.get("extras.hello"),
"world",
"it can supply extra information"
);
const fruitCols = fruits.objectAt(0).get("colors");
assert.equal(fruitCols.length, 2);
assert.equal(fruitCols[0].get("id"), 1);
assert.equal(fruitCols[1].get("id"), 2);
const fruitCols = fruits.objectAt(0).get("colors");
assert.equal(fruitCols.length, 2);
assert.equal(fruitCols[0].get("id"), 1);
assert.equal(fruitCols[1].get("id"), 2);
assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker");
});
assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker");
});

View File

@ -35,26 +35,23 @@ QUnit.test("createFromJson array", assert => {
);
});
QUnit.test("findByUsername", assert => {
return UserBadge.findByUsername("anne3").then(function(badges) {
assert.ok(Array.isArray(badges), "returns an array");
});
QUnit.test("findByUsername", async assert => {
const badges = await UserBadge.findByUsername("anne3");
assert.ok(Array.isArray(badges), "returns an array");
});
QUnit.test("findByBadgeId", assert => {
return UserBadge.findByBadgeId(880).then(function(badges) {
assert.ok(Array.isArray(badges), "returns an array");
});
QUnit.test("findByBadgeId", async assert => {
const badges = await UserBadge.findByBadgeId(880);
assert.ok(Array.isArray(badges), "returns an array");
});
QUnit.test("grant", assert => {
return UserBadge.grant(1, "username").then(function(userBadge) {
assert.ok(!Array.isArray(userBadge), "does not return an array");
});
QUnit.test("grant", async assert => {
const userBadge = await UserBadge.grant(1, "username");
assert.ok(!Array.isArray(userBadge), "does not return an array");
});
QUnit.test("revoke", assert => {
QUnit.test("revoke", async assert => {
assert.expect(0);
const userBadge = UserBadge.create({ id: 1 });
return userBadge.revoke();
await userBadge.revoke();
});