Vitest With laravel-vite-plugin on Bitbucket Pipelines

Jim O'Halloran • November 15, 2022

amazon-web-services javascript laravel

So I've got a web application I've been working on for a while. Laravel, Inertia, Vue, Jetstream, all that good stuff. I've set up the front end build with Vite according to the standard docs , which means I've got laravel-vite-plugin installed.

Today, I decided to dip my toes into Javascript Unit Testing for the first time in many years. Starting at the Vue docs, it looks like Vitest is the recommended approach with an easy to follow recipe . I've already got Vite, so that seems like a good fit.

Everything went well, I got a couple of simple unit tests up and running, and decided to try and hook it into my Bitbucket Pipelines based CI system.

I kept getting the following error whenever I ran npm test (in CI):

⎯⎯⎯⎯⎯⎯ Unhandled Error ⎯⎯⎯⎯⎯⎯⎯
Error: You should not run the Vite HMR server in CI environments. You should build your assets for production instead.
    at ensureCommandShouldRunInEnvironment (/opt/atlassian/pipelines/agent/build/node_modules/laravel-vite-plugin/dist/index.js:169:15)
    at config (/opt/atlassian/pipelines/agent/build/node_modules/laravel-vite-plugin/dist/index.js:54:13)
    at runConfigHook (file:///opt/atlassian/pipelines/agent/build/node_modules/vite/dist/node/chunks/dep-4da11a5e.js:63556:31)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async resolveConfig (file:///opt/atlassian/pipelines/agent/build/node_modules/vite/dist/node/chunks/dep-4da11a5e.js:63076:14)
    at async createServer (file:///opt/atlassian/pipelines/agent/build/node_modules/vite/dist/node/chunks/dep-4da11a5e.js:62092:20)
    at async createVitest (file:///opt/atlassian/pipelines/agent/build/node_modules/vitest/dist/chunk-vite-node-externalize.72a4d20b.js:11390:18)
    at async startVitest (file:///opt/atlassian/pipelines/agent/build/node_modules/vitest/dist/chunk-vite-node-externalize.72a4d20b.js:11501:15)
    at async start (file:///opt/atlassian/pipelines/agent/build/node_modules/vitest/dist/cli.js:697:17)

The vitest docs were silent on the issue, and everything I read suggested that if the CI environment variable was set vitest should just run once and not use watch mode automatically. A lot of Googling didn't return anything about BitBucket Pipelines specifically, and I didn't find any guides for running vitest on CI. It seemed from the docs that it should "just work", and the lack of blog posts, etc on the internet kind of suggested others hadn't run into problems either.

Reading the stack trace carefully I notice laravel-vite-plugin gets a couple of mentions, is that something unique about my environment?

TL;DR

If you're using vitest and laravel-vite-plugin together and looking to run vitest in a CI environment run LARAVEL_BYPASS_ENV_CHECK=1 npm test in CI instead of npm test.

Digging In

The first thing I did was open index.js in my node_modules/laravel-vue-plugin\dist folder, and scroll to line 54, which is the first mention of the Laravel plugin in the stacktrace.

This is a simple call to ensureCommandShouldRunInEnvironment (the next line in the stack trace).

ensureCommandShouldRunInEnvironment(command, env);

There's nothing around it that looks incriminating, and the stack trace ends in that function, so obviously it was called. The stack trace ends at line 169 in the same file, what's there?

throw Error('You should not run the Vite HMR server in CI environments. You should build your assets for production instead.');

Ok, so that's the error we're seeing, no surprise the stack trace ended there. Lets expand our search a little, what context is around it:

if (typeof env.CI !== 'undefined') {
    throw Error('You should not run the Vite HMR server in CI environments. You should build your assets for production instead.');
}

Ok, the error gets triggered if the CI environment variable is set. BitBucket's Default Variables does include CI, so that's definitely why it's being triggered.

Working my way back through the function, the rest of it is similar environment variable checks and errors for Vapor, Forge and Envoyer, so that's not going to bother us.

Right at the top of the function what do I see?

if (command === 'build' || env.LARAVEL_BYPASS_ENV_CHECK === '1') {
    return;
}

If the command is build or a bypass environment variable is set, we bail early. This could be our escape hatch! I could modify that line to do something vitest specific (which would be the long term fix but awkward for now, I'd need to fork the plugin). Simplest for now is the environment variable workaround.

I wanted a "copy and paste" solution that I could put in git and transport to other projects easily if needed, so setting the environment variable via Bitbucket's environment features didn't appeal to me. I also only needed the bypass for this one command. So in the script: block in my bitbucket-pipelines.yml file I changed my npm test line to:

LARAVEL_BYPASS_ENV_CHECK=1 npm test

Which got my tests up and running in CI.

Conclusion

At the time of writing, vitest is at version 0.25.2, and laravel-vite-plugin is 0.7.0. I think ultimately this is a bug in laravel-vite-plugin (I've filed #164 so in time this might be fixed).

This isn't a super power, or some kind of magic, I'd never even looked at the laravel-vite-plugin code before today, and my familiarity with front end tooling in general is pretty minimal. But by reading the error message carefully I was able to pinpoint and find a workaround to an issue that's likely fairly unique to my environment. Remember, this stuff is all open source, the code is often the best source of documentation.

Read The Source Luke