DEV Community

Cover image for Speed up E2E tests for Vite-based apps
Stefano Magni
Stefano Magni

Posted on

Speed up E2E tests for Vite-based apps

Before Vite we used long bundling waitings, and applications loaded instantly in the browser. Now, the bundling waitings are over, and apps load slower. You can get an idea of what is changed in the following image.

An overall idea of how Rollup and Vite work, with a benefit for the developer

The overall Developer's experience is excellent (you can find some benchmarks in my Migrating a 150K LOC codebase to Vite and ESBuild: is it worthwhile? article) because apps and HMR are way faster, but what about who needs to load the app more than once? Like the Cypress' E2E tests, for example. Cypress reloads the app before every test. Hence is going to pay the cost of loading the app multiple times, something like the image.

The same overall idea of how Rollup and Vite work, with a clear disadvantage for Cypress

Is there a way to fix it? Can we have the best from using Vite and from using Rollup simultaneously? The answer is yes, and we can do that for free!

Enabling Rollup watcher inside Vite

Vite leverages Rollup for the production build and allows enabling the Rollup watcher. The goal is

  1. Creating a custom Vite configuration to use the Rollup watcher
  2. Starting a web-server to serve the bundled-by-Rollup application
  3. Avoiding to interfere with the served-by-Vite application (so both the application can run simultaneously)

1. Creating a custom Vite configuration to use the Rollup watcher

The following is the bare minimum settings enabling the Rollup watcher and also speed it up as much as possible (thanks to this Reddit post by @securisec).

// rollup.config.ts

export default defineConfig(config => {
  const { mode } = config

  return {
    // ... Rest of the configuration...

    build: {
      // Enable Rollup watcher @see https://vitejs.dev/config/#build-watch
      watch: {},

      // Opt for the fastest build
      target: 'esnext',
      minify: false,
      rollupOptions: { ...config.build.rollupOptions, treeshake: false },

      outDir: './dist-rollup/',
    },
  }
})
Enter fullscreen mode Exit fullscreen mode

Please note that since the Rollup-specific configuration is inside the build object, we must run vite build to start Rollup. The following is the script I created in our package.json

{
  "rollup:dev": "yarn vite build --config rollup.config.ts",
}
Enter fullscreen mode Exit fullscreen mode

2. Starting a web-server to serve the bundled-by-Rollup application

Please remember that enabling the Rollup watcher does not mean starting a we-server serving the generated assets.

Here Vite comes in handy with its preview command. Essentially, we need to launch both vite build and vite preview simultaneously.

These are the scripts I created in our package.json

{
  "rollup:start": "yarn rollup:dev & yarn rollup:preview",
  "rollup:dev": "yarn vite build --config rollup.config.ts",
  "rollup:preview": "yarn vite preview --config rollup.config.ts",
}
Enter fullscreen mode Exit fullscreen mode

rollup:start is now the reference script to launch Rollup.

3. Avoiding to interfere with the served-by-Vite application (so both the application can run simultaneously)

To achieve that, I need to

  1. Load the Vite config and change the minimum settings
  2. Use a different preview port

I then transformed the vite.config.js file to expose the configuration generator

-export default defineConfig(config => {
+export default defineConfig(generateConfig)
+export function generateConfig(config) {
Enter fullscreen mode Exit fullscreen mode

and rollup.config.ts now does only the following

import { generateConfig } from './vite.config'

export default defineConfig(({ mode }) => {
  const config: UserConfig = generateConfig({ mode })

  config.preview = {
    port: 5004,
  }

  config.build = {
    // Enable Rollup watcher @see https://vitejs.dev/config/#build-watch
    watch: {},

    // Opt for the fastest build
    target: 'esnext',
    minify: false,
    rollupOptions: { ...config.build.rollupOptions, treeshake: false },

    outDir: './dist-rollup/',
  }

  return config
})
Enter fullscreen mode Exit fullscreen mode

That's all, you can now choose between Vite and Rollup as your development tool (or use both simultaneously). Our package.json file (we use concurrently and TSC) now is

{
  "vite:start": "concurrently --names \"VITE,TSC\" -c \"bgMagenta.bold,bgBlue.bold\" \"yarn vite:dev\" \"yarn ts:watch\"",
  "vite:dev": "yarn vite",
  "vite:build": "vite build",
  "vite:build:preview": "vite preview",
  "vite:clearcache": "rimraf ./node_modules/.vite",
  "________________________________________________________________________________________": "",
  "rollup:start": "yarn rollup:dev & yarn rollup:preview",
  "rollup:dev": "yarn vite build --config rollup.config.ts",
  "rollup:preview": "yarn vite preview --config rollup.config.ts",
}
Enter fullscreen mode Exit fullscreen mode

FAQ

What's the threshold? When do you start gaining the advantages of using Rollup instead of Vite?
Based on my empiric benchmarks, if you reload the same app more than three times, Rollup gets you to save time.

Can I use both Vite and Rollup simultaneously?
Yes, they are not mutually exclusive, and by using a different port, you can access both web applications.

Would disable Rollup sourcemap increase speed?
Based on my benchmarks, no. Anyway, I disabled it in our main application due to a "JS heap out of memory" error.

How can I migrate my application to Vite?
I shared our migration experience in the "Migrating a 150K LOC codebase to Vite and ESBuild" article.

If you follow a similar pattern or have similar solutions, please drop a line in the comments with your feedback.

p.s. this is the link to the Excalidraw file used above.

Top comments (0)