Use foremanReact directly in plugin tests

Hello Everyone,

In Katello and other plugins, we use Foreman JS helper functions and React components that are provided by an alias foremanReact. This is set up by webpack and is used like a normal npm package, for example here in Katello.

Because this of the way this is structured, we don’t use foremanReact in tests, rather mock it’s usage

This has been working fine, but with new React pages being added, we would like to take advantage of the API middleware and other utilities provided by Foreman. The problem is, anything from formeanReact will have to be mocked during testing and not fully reflect the way the components will function in their actual usage. This is especially evident with react-testing-library (see Using react-testing-library in Katello for the discussion introducing that library), which tests components fully rendered with child components and redux logic present, meaning the code used from foremanReact is expected to be there.

And besides the immediate use-case, we plan to centralize more JS code in Foreman as time goes on. When large parts of our code are coming from foremanReact, it will only get harder to mock anything that comes from there, and we will likely duplicate logic.

I see not being able to use foremanReact in testing at odds with two of the initiatives we have on the UI side of things:

  • Centralize React components and logic to Foreman so we can standardize and share across plugins
  • Test the application as a user would use it. (fully connected and rendered components)

So what can we do about this?

I see two options:

Option 1: Publish the foremanReact JS code as an npm package.
pros: Use foremanReact as a normal npm package, which is easily used in testing.
cons: More release-juggling, coordinating across repos, yet-another-repo

Option 2: Allow jest to use foremanReact directly from Foreman. I have a Katello PR adding this.
pros: No change in Foreman, use the code directly from Foreman.
cons: Changes in Foreman could break plugin test. Assumes Foreman is in a sibling directory during testing.

Note with either option, parts of foremanReact can still be mocked, same as any other package. The difference is you don’t have to mock, it can be used directly.

I wanted to open up this conversation to a larger audience in case any other plugin authors or Foreman devs would like to weigh in. I think this is something we have to solve if we want to continue to centralize and standardize our UI code in Foreman.

2 Likes

Here is a quick straw poll to get a feel for opinions:

  • Option 1: npm package
  • Option 2: jest uses foreman code
  • Do nothing
  • I have another suggestion

0 voters

Great post @John_Mitsch !
I think that for the short term option 2 is great.
Though in my opinion, option 1 - moving the components to a new repo will have many more benefits:

  • faster development
  • not breaking plugins with core changes
  • plugins will upgrade when they are ready
  • fresh code base with no technical debt

I think we can learn from theforeman/foreman-js repo
since it’s working great and I think that even in packaging prespective it made things easier

This sounds like you’re going to duplicate multiple foremanReact instances: one for every plugin. This is a lot of duplication and can make things a lot slower. What happens when two plugins need to interact and use different versions? It sounds like a recipe for disaster to me.

In the same way that theforeman npm packages works today, plugins will need to upgrade to be able to work properly with other plugins, though it won’t be a surprise that something broke or changed without they are being aware…
I didn’t say it’s gonna be easy to implement… but worth a try

1 Like

Is this possible right now? Plugins use the foreman version of packages, for example Katello uses @theforeman/vendor from Foreman’s package.json. Can you have different versions of the same package in Foreman and Katello?

I think when used with foreman, it will use what version that foreman uses anyway,

But for development, such as tests and storybook, if we could import the ForemanReact code from npm, it will solve what you are talking about, right?
cc @sharvit

If we publish it as an npm package, I assume we will use it as any other npm package. This means the runtime dependency will come from Foreman if Foreman is using the package (which seems like a safe assumption). Are you suggesting plugins using a different dependency during testing? It seems like we should be testing with the runtime dependency versions.

Having foremanReact as a published npm package will solve the original issue I detailed, but your previous comment suggested that

which I don’t see being the case with the current setup, Foreman upgrading the package will do so for all the plugins. It’s an important distinction if we were to go this route that I think we should make clear.

If plugins have to use the same version of foremanReact as Foreman and we do find a way to make jest use the code itself from Foreman, I would question the need for a separate package. I don’t want to discount it all together, but if only one application (Foreman) is using the package, and plugins use the package through Foreman, its easy to wonder why we would make the abstraction in the first place.

I like the approach of having small units where each unit has its own role,
and looking at other modern projects as SaaS, where you have a repo for the API and a repo for the UI
feels like the direction to go.

In this way we can share component and logic across all of our plugins easily,
maybe we can even tweak the versioning juggling by not releasing a major version greater than 1, for example ^1.0.0, so everyone will be on the same page.

I do agree it can be tough to set this two repos to work together for the first time,
but I think it can provide us a lot of benefits.
with every minor upgrade we will specify what changes or features were added
so it will be easier to debug in plugins when something brakes.

what are your thoughts?

1 Like

Strongly disagree. Maybe you should ask the Katello developers about their experiences with Bastion.

IMHO splitting into microservices is as much about organization as about the technical parts. They reflect the teams that work on some areas. In Foreman we are an open source project and everyone can work on all areas so introducing small pieces where only a few people work on those parts leads to balkanization of the platform.

A while back we saw the exact same problem: the UI team was working on the UI and the backend team was working on the backend. There was little to no communication between the two. That led to frustrations from both sides and an us vs them situation. Only once we started to integrate and let both teams see the full stack, there came more of an understanding of the bigger picture.

In essence we have that right now, at least with Ruby versions. By having that all as part of Foreman itself and having all execution run from there. Plugins are what the name says: you plug it into Foreman itself. It’s useless without it.

The repo exists already IMHO: it’s called foreman.git. Now I understand NPM is very inflexible and an absolute pain, but I think in the past @sharvit suggested to use npm link to install plugins. Perhaps Yarn 2’s workspaces would actually solve this if you’d setup symlinks/repos in that directory.

1 Like

This is something I feel I must comment on.

A lot of what is considered “Best Practice” and tooling in the JS world today assumes a SaaS-based application, where there is one production environment that is completely controlled by the developers, and is commonly updated multiple times every week.

Foreman is architecturally different, in three very significant manners:

  1. We don’t have a SaaS offering that is under our control for deployment. Foreman is installed locally at user’s data centers, and is delivered as packages with all assets pre-compiled, and is usually not updated more than once in a few months.
  2. Foreman has a large number of plugins, that can optionally be installed or not. Many of these plugins are developed in-house by users who aren’t part of the Foreman development team. This means we don’t have a single configuration we need to support, but rather a wide range of permutations that should work.
  3. Foreman normally runs on a beefy server, that most users connect to over a fast connection. It is also used by very few users, tens at the largest instances. For comparison, SaaS usually tries to scale for thousands of concurrent users connecting over a slow internet connection.

This doesn’t mean we can’t use modern tooling or practices, but we need to keep these points in mind when we do. What is considered best practice in the SaaS world, might sometimes actually be a really bad idea in our use case, and we have to be careful that we evaluate our decisions with that in mind.

3 Likes

This feature is now merged in Katello which means we can use foremanReact code in our testing. Fixes #29637 - Allow jest test to use foremanReact · Katello/katello@cb8ed4d · GitHub

Any other plugins who would like to do the same can follow the same pattern. Feel free to ask any questions here.

These are all great points and the reality that we have to face with our project and the modern JS ecosystem. A lot of these topics were touched on in An Independent UI for Foreman and Plugins - Discussion and Investigation - #20 by TimoGoebel, where our plugin system and having the ability to use in-house plugins installed at runtime make having an independent UI difficult. This thread is getting a bit off topic from the original topic, so I encourage any further architecture discussions to happen in the thread I linked or a new topic :slightly_smiling_face:

I hope being able to use foremanReact in plugin tests will encourage more centralization and standardization of JS code in Foreman and make it easier for plugins to re-use code.

2 Likes