Importing a model or component from a plugin into a custom plugin: tests fail

Hello,
We are using Discourse version 3.5.0.
We have our own plugin in which we override various Discourse elements and add our own features.

In this plugin, we want to add the event creation icon to the composer’s toolbar. To do this, we need to import:

  • the model DiscoursePostEventEvent located at plugins/discourse-calendar/assets/javascripts/discourse/models/discourse-post-event-event.js

  • the component PostEventBuilder located at plugins/discourse-calendar/assets/javascripts/discourse/components/modal/post-event-builder.js

Furthermore, we have written integration tests for our features, which we have placed in our plugin under the /test directory.

import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";

acceptance("TOTO", function (needs) {
  test("coopaname integration - composer", async function (assert) {
    await visit("/");
    assert.equal(1, 1, "OK");
  });
});

We are able to do what we want, but the tests fail regardless of the import method.

Import at the top of the file

When we import our model and component using the import method at the top of the file:

import DiscoursePostEventEvent from "discourse/plugins/discourse-calendar/discourse/models/discourse-post-event-event";
import PostEventBuilder from "discourse/plugins/discourse-calendar/discourse/components/modal/post-event-builder";

Everything works fine at runtime, but when we run our test, it is not executed.

image

image

Require at the top of the file

When we import our model and component using the require method at the top of the file:

const DiscoursePostEventEvent = require("discourse/plugins/discourse-calendar/discourse/models/discourse-post-event-event").default;
const PostEventBuilder = require("discourse/plugins/discourse-calendar/discourse/components/modal/post-event-builder").default;

Everything works fine at runtime, but when we run our test, it is not executed.

image

image

This produces the same result as with import.

Require in the body of the file

When we import our model and component using the require method in the body of the function:

import { withPluginApi } from "discourse/lib/plugin-api";

function initializeEventBuilder(api) {
  const DiscoursePostEventEvent =
     require("discourse/plugins/discourse-calendar/discourse/models/discourse-post-event-event").default;
  const PostEventBuilder =
     require("discourse/plugins/discourse-calendar/discourse/components/modal/post-event-builder").default;

  // ... rest of the code
}

export default {
  name: "add-custom-create-event-button",
  initialize(container) {
    withPluginApi(initializeEventBuilder);
  },
};

Everything works fine at runtime, but when we run our test, it fails.

image

The model was not found.

In short, we would like to know how to correctly import models, components, etc. from other plugins so that we can run integration tests.
Thank you!

2 Likes

Hello @Marine

It seems the issue lies in the wrong settings being used in the needs.settings block of our code:

acceptance("Some tests", function (needs) {
  needs.settings({
    // This won't enable the right settings, so the calendar plugin won't be loaded, and the files we need won't be available
    // discourse_post_event_enabled: true, 
    // 
    // Enabling the calendar will solve the issue:
    calendar_enabled: true

    our_plugin_enabled: true
  });


  test("The test", async function (assert) {
    await visit("/");
    assert.equal(1, 1);
  });
});

Well, this is not true. As the plugin overrides the event form, we need to set discourse_post_event_enabled to true. Back to initial situation where the import in our plugin fails.

Any idea?

When you test a plugin in qunit, we only load the JS for that specific plugin. That helps to avoid surprise issues when multiple plugins interact with each other. It also helps to ensure that a plugin doesn’t unexpectedly become dependent on another.

In your case, it sounds like the interaction is deliberate, and you always expect your users to have discourse-calendar enabled. In that case, you can add some config to an about.json file which will make the discourse-calendar JS available in the tests of your own plugin. Here’s an example:

^^ in this case it’s adding discourse-assign. For your case, you would make it discourse-calendar

4 Likes

Thanks! I didn’t know about about.json and it solved the issue. Now, it’s up to us to re-create a fully functional context :wink:

Thanks again!

2 Likes

You may also be interested in system specs for end-to-end testing. That way, the test is run against a real Discourse server, so you don’t have to fake all of the network requests for core & both plugins.

The downside is that they’re much slower. So best to keep any unit/component tests in qunit if you can.

2 Likes

Thanks for the tip!

I’m really more comfortable with system specs as I usually use RSpec or Cucumber to test my Rails apps :slight_smile:

I’ll see what’s worth testing with QUnit or RSpec, depending on the test case.

For now, I managed to reproduce the frontend bug we wanted to fix, so I’ll dig a bit more in QUnit tests.

2 Likes