How to test that CSS is being included by an addon

I’m trying to add a test to Ember CLI Tailwind that verifies the addon correctly builds tailwind.css and adds it to the host app.

Some of our existing tests verify that a vendor css file is included, or that certain JS modules are included. I put a debugger here and poked around the addon and output variables, but couldn’t find where I might assert against the built css file.

The file that ends up in the build is tailwind.css which needs to be @import’ed somehow by the host app. Ideally my test would be,

GIVEN a new ember app with config of `ENV['ember-cli-tailwind'].buildTarget = 'app'`
WHEN I build the app
THEN I expect the file `tailwind.css` to be in the build

Also, even better would be to assert against the contents of that css file, for example to ensure the build was using the correct config values (e.g. colors) that the host app provides.

Thanks for any help!

I recently tried adding a test to ember-intl to assert that the addon’s trees did the right thing inside an app.

The test used broccoli-test-helper and looked something like this: https://gist.github.com/lennyburdette/050ca882b3eb03da56916bce42da6649

It was a bit more difficult than I would have liked, especially since I had to reach into Ember CLI’s internals to reset an addon cache between tests.

I think it’d be great to:

  • have a helper that builds a minimal application fixture so I don’t have to recreate the basic structure of an Ember CLI app.
  • have a helper that handles symlinking NPM packages into the temp directory so the build has its dependencies.
  • be able to pass an absolute path to new EmberApp() so that I don’t have to mess with process.cwd.
  • have a public API for resetting internal caches.

But it otherwise worked really well!

Awesome, this got me up and running! Looks like addon.toTree() takes a while (hence the longer timeout). But very cool to be able to assert against the final build like this.

Thank you so much for the code!

Could you talk a little bit more about the fixture lines work?

I got the basic test working, but I’m trying to figure out if I can test three different scenarios. The different scenarios depend on different configurations that other apps or addons may have.

For example:

  • GIVEN an addon with a /addon/tailwind directory
  • GIVEN an addon with a /tests/dummy/app/tailwind directory
  • GIVEN an app with a /app/tailwind directory

Can I use fixtures to set these situations up?

Yeah, definitely. The broccoli-test-helper package wraps fixturify - npm — take a look at that package for more examples on creating files for your tests.

The “application-fixture.js” in my gist is just the simplest example. You can have multiple fixture objects that describe various file layouts. I have made a wrapper function like:

function loadFixture(input, fixtureName) {
  // this loads a fixture by filename and writes the files to disk
  input.write(require(`./fixtures/${fixtureName}`)());

  fs.symlinkSync(path.join(root, 'node_modules'), path.join(input.path(), 'node_modules'));
  fs.symlinkSync(ADDON_ROOT, path.join(input.path(), 'ember-cli-tailwind'));

  process.chdir(path.join(input.path(), 'application'));
}

test('including tailwind files from app/tailwind', co.wrap(*function() {
  const input = yield createTempDir();
  loadFixture(input, 'app-with-tailwind-directory');
  ...
});

Does that help?

That does help… I’m slowly learning :slight_smile:

So yeah – I now have a passing test passing in the config. So my next step is move this instead to an external file, because ultimately my goal here is to implement a new feature where the addon auto-discovers the presence of a tailwind directory in the dummy app, and uses that to build the appropriate CSS files (instead of requiring the user to specify this in config). But I want to be able to test it.

So, I’m hoping to have something like a /node-tests/ fixtures directory, and I could put different “scenarios” in there. To start, I’d have some files simulating a dummy app? Or, I guess I could use the existing dummy app for that scenario…

The other two scenarios are an addon with an addon/tailwind directory, and a host app with an app/tailwind directory. I want to be able to test that my index.js works correctly for all three cases.

If I understand your code correctly, you need to create a temp directory as part of the Broccoli build, and then you write a copy of your test fixtures to that directory so the broccoli build has access to them. At that point I should be able to use the debugger in my index.js file during a test, and explore the Broccoli build as if an addon or host app were running. Does that sound right?

That does sound right. I agree that a node-tests/fixtures/ directory is a great idea. If you’re interested it testing out how your addon uses dummy app files, a fixture file like node-tests/fixtures/dummy-app-with-tailwind.js could look like:

module.exports = function() {
  return {
    application: {
      app: {
        styles: {
          'app.css': ''
        },
        'index.html': ''
      },
      config: {
        'environment.js': "module.exports = function() { return { modulePrefix: 'application' } };"
      },
      tests: {
        dummy: {
          app: {
            tailwind: {
              'styles.css': 'example content'
            }
          }
        }
      },
      'ember-cli-build.js': 'module.exports = {};',
      'package.json': JSON.stringify({
        name: 'application',
        devDependencies: {
          'ember-cli': '*',
          'ember-cli-babel': '*',
          'ember-cli-htmlbars': '*',
          'ember-engines': '*',
          'ember-source': '*',
          'loader.js': '*'
        },
        'ember-addon': {
          paths: ['../ember-cli-tailwind']
        }
      }),
    }
  }
};

Running new EmberApp().toTree() will discover the addons needed by the application in the fixture (including yours, because it’s in the “ember-addon.paths” key in package.json) and run their lifecycle hooks. So yeah, you’ll be able to put a debugger in your index.js.

Ah I see. Trying it out now!

Quick question: what’s with the co package? I’ve never seen or used that before.

Another question – what’s with mocha/chai in node-land? Is that just the convention here?

Feels weird I have to learn a new assertion syntax and so on.

I’m just following the examples in the GitHub - broccolijs/broccoli-test-helper: Test helpers for BroccoliPlugins that make testing build and rebuild behavior dead simple and expect diff friendly. readme. Co is a library for providing an async/await-like experience in earlier versions of node. And I would bet you could use whatever assertion library you want.

Maybe @rwjblue could chime in here? We should probably collect our pain points and turn them into improved APIs for the whole addon testing experience.

A few things that seem like obvious fixes we need to do (based on this thread and the other one that @lennyburdette started a little while back), and I think @lennyburdette listed them nicely in:

Haha, agreed! But you don’t have to! You can easily use QUnit based tests in Node-land. Take a look at this test file which uses the “standard” broccoli-test-helper system but with QUnit.

I cheekily did this, but you definitely don’t have to:

const describe = QUnit.module;
const it = QUnit.test;

My own words came back to haunt me! :smile:

I had a few hours on a plane yesterday so I bundled up my testing patterns into a package:

Please take a look! I haven’t published it yet—I’ll need to set up CI and move it to the Square github account before doing so.