Strange QUnit behaviour?: test failing because setting value doesn't survive

AI-generated summary

merefield is experiencing a strange issue with QUnit tests failing due to a plugin setting value changing unexpectedly. The value of location_topic_default is changing from “user” to “none” during the test run.

david investigated the issue and found that the problem is due to the way initializers work in QUnit tests. The siteSettings object is being obtained by the initializer and used in a modifyClass call, but this call is only executed once in the whole test suite. As a result, subsequent tests are stuck with the site settings from the first test.

david suggested a solution to use a site settings reference at runtime rather than at initializer time, and provided a diff to fix the issue.

merefield thanked david for the explanation and solution, and noted that there is another related issue with the currentUser variable being defined in the initializer. merefield plans to switch to using the user property from the Composer model to avoid this issue.

I have a strange issue with QUnit.

This test is extremely simple and should be straightforward … but …

A plugin setting is changing from those I have set up.

When the javascript runs as a result of the Composer loading, the setting value set earlier in the acceptance block is no longer the same!

It should be “user” as set in the test code, but if console logged, this is what I see, and why the test fails:

Am I doing something stupid here? Why is the value of location_topic_default changing from user to none?

Please note the default:

The scope of needs.settings is presumably correct here?

It’s almost as if the needs.settings are running out of order and beyond the acceptance scope …

I took this for a spin locally. Looks like there a few factors at play:

The siteSettings object you’re referencing is being obtained by the initializer, and then used in a modifyClass call:

Initializers are re-run for each test. Problem is, we have no way to ‘reset’ any modifyClass calls that were made by previous tests. Our solution is the pluginId parameter - it means that only the first modifyClass call in the whole test suite is actually used. Calls to modifyClass from initializers in future tests are ignored.

Normally that’s fine - code inside a modifyClass invocation doesn’t tend to change in each test run. However, in this case you’re referencing the siteSettings reference from the initializers scope.

The tl;dr here is: in tests, this implementation means that the modifyClass will be stuck with the site settings from whichever test was the first to run.

The solution is to use a siteSettings reference ‘at runtime’ rather than at ‘initializer’ time. We can use the one from model:composer itself. This diff gets the tests passing for me:

diff --git a/assets/javascripts/discourse/initializers/location-edits.js.es6 b/assets/javascripts/discourse/initializers/location-edits.js.es6
index 19e50c0..9d5f882 100644
--- a/assets/javascripts/discourse/initializers/location-edits.js.es6
+++ b/assets/javascripts/discourse/initializers/location-edits.js.es6
@@ -83,7 +83,7 @@ export default {
         @observes("draftKey")
         _setupDefaultLocation() {
           if (this.draftKey === "new_topic") {
-            const topicDefaultLocation = siteSettings.location_topic_default;
+            const topicDefaultLocation = this.siteSettings.location_topic_default;
             if (
               topicDefaultLocation === "user" &&
               currentUser.custom_fields.geo_location &&

David, thanks so much! That’s quite a gotcha!

David,

FYI

There’s another issue here which I suspect is for the same reason:

currentUser is also defined in the initialiser.

If the wrong test runs second this is evaluated and this is no longer defined so the tests when run together can fail “half” of the time.

I think the Composer model has user so I’ll switch to that …