npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cypress-clojurescript-preprocessor

v0.2.0

Published

ClojureScript preprocessor for Cypress using shadow-cljs

Downloads

238

Readme

cypress-clojurescript-preprocessor

A Cypress preprocessor for ClojureScript, which uses Shadow CLJS for processing Cypress tests written in ClojureScript.

The plugin works by inspecting the Cypress integrationFolder (by default, cypress/integration) for ClojureScript files (*.cljs, e.g. cypress/integration/my_app/app_test.cljs -> (ns my-app.app-test)) and generates a shadow-cljs.edn configuration file with a build for each test file. This configuration is then used to compile the tests into Javascript before submitting to the browser.

The shadow-cljs server is kept running while the Cypress runner is active. When a test is run, the test file is watched for changes and a recompile is done if the test file is changed and Cypress is notified to rerun the test. The first compile is a bit slow, but subsequent compiles are fast (the plugin uses shadow-cljs compile command, which defaults to optimizations: none).

Example usage

  1. Create a project

    $ mkdir cypress-cljs-sample
    $ cd cypress-cljs-sample
    $ echo {} > package.json
  2. Install Cypress

    npm install cypress --save-dev
  3. Install cypress-clojurescript-preprocessor

    npm install cypress-clojurescript-preprocessor
  4. Configure ClojureScript preprocessor

    $ mkdir -p cypress/plugins
    $ cat << EOF > cypress/plugins/index.js
    const makeCljsPreprocessor = require('cypress-clojurescript-preprocessor');
    /**
     * @type {Cypress.PluginConfig}
     */
    module.exports = (on, config) => {
      on('file:preprocessor', makeCljsPreprocessor(config));
    };
    EOF

    This will delegate files other than *.cljs to the default Browserify preprocessor. If you need to run other preprocessors, then combine them with for example:

    module.exports = (on, config) => {
      const browserifyPreprocessor = makeBrowserifyPreprocessor();
      const cljsPreprocessor = makeCljsPreprocessor(config);
      // Use the default Browserify preprocessor for files other than *.cljs
      on('file:preprocessor', (file) => file.filePath.endsWith('.cljs') ? cljsPreprocessor(file) : browserifyPreprocessor(file));
    };
  5. Write test in ClojureScript

    $ mkdir -p cypress/integration/examples
    $ cat << EOF > cypress/integration/examples/window.cljs
    (ns examples.window
      (:require-macros [latte.core :refer [describe beforeEach it]]))
    
    (def cy js/cy)
    
    (describe "Window"
      (beforeEach []
        (.visit cy "https://example.cypress.io/commands/window"))
      (it "cy.window() - get the global window object" []
        (.should (.window cy) "have.property" "top")))
    EOF
  6. Run test

    ./node_modules/.bin/cypress open

REPL in Cypress

You can active REPL into a test in the browser that Cypress is controlling.

  1. Lookup NREPL port used by the shadow-cljs server

    $ cat .preprocessor-cljs/.shadow-cljs/nrepl.port
    50796
  2. Connect to the NREPL server and list builds

    shadow.user> (shadow/get-build-ids)
    (:npm :local :window_foo :window)
  3. Open a test in the Cypress runner

  4. Start shadow-cljs watch on the test that is open in the Cypress runner

    shadow.user> (shadow/watch :window)
    [:window] Configuring build.
    [:window] Compiling ...
    [:window] Build completed. (119 files, 0 compiled, 0 warnings, 1.22s)
    :watching

    The build-id is a keyword of the test file name

  5. Start a ClojureScript repl

    shadow.user> (shadow/repl :window)
    To quit, type: :cljs/quit
    [:selected :window]
    cljs.user> (js/alert "plop") ;; To try out that it works

Now you can use the slightly undocumented now command to execute Cypress command immediately, for example, to select an option:

cljs.user> (-> (.now js/cy "get" "#some-opt")
               (.then (fn [el]
                        (.now js/cy "select" el "two"))))
#object[Promise [object Promise]]
  1. When done, exit the repl and stop shadow-cljs watch for the repl build

    cljs.user> :cljs/quit
    Exited CLJS session. You are now in CLJ again.
    :cljs/quit
    shadow.user> 
    (shadow/stop-worker :window)
    Worker shutdown.
    :stopped

Some references to the now command:

  • https://github.com/cypress-io/cypress/issues/6080#issuecomment-570481923
  • https://docs.cypress.io/guides/guides/debugging#Run-Cypress-command-outside-the-test
  • https://github.com/cypress-io/cypress/issues/8195
  • https://github.com/cypress-io/cypress/issues/3636#issuecomment-511315778

Configuration

Shadow CLJS configuration override

The Shadow CLJS configuration used by the preprocessor may be overridden via a shadow-cljs-override.edn file, which is merged on top of the default configuration with meta-merge. By default, [mocha-latte "0.1.2"] and [chai-latte "0.2.0"] are included in the shadow-cljs.edn configuration used by the preprocessor.

Shared namespaces

The working directory of the shadow-cljs compiler that is driven by the preprocessor is the cypress folder. Because of this, in order to include shared code that is in a folder at the same level as the cypress directory, use a relative (or absolute path) e.g.

$ tree -I "node_modules" -P common.cljs\|window.cljs
.
├── cypress
│   ├── fixtures
│   ├── integration
│   │   └── examples
│   │       └── window.cljs
│   ├── plugins
│   └── support
└── src ;; shared code directory
    └── net
        └── tiuhti
            └── common.cljs
$ cat shadow-cljs-override.edn
{:source-paths ["../src"]} ;; path relative to `cypress` directory
$ head -3 cypress/integration/examples/window.cljs
(ns examples.window
  (:require-macros [latte.core :refer [describe beforeEach it]])
  (:require [net.tiuhti.common :as common]))  ;; require namespace in the `src` directory

Dependencies used by the shared code would have to be specified in the :dependencies key in shadow-cljs-override.edn file.

Development

Here's some notes on how to develop this library.

Shadow-cljs repl

The preprocessor is written in ClojureScript and compiled to a npm library with the help of shadow-cljs. To develop the library in a ClojureScript repl:

  1. Start node repl via shadow-cljs node-repl
  2. Connect to the nrepl server for example from Emacs with cider: cider-connect
  3. In nrepl at the shadow.user> prompt, switch to the node repl via: (shadow/repl :node-repl)

Use locally compiled version

To use a local version of the preprocessor, point the client project to the local clone of the preprocessor:

{
  "devDependencies": {
    "cypress": "^7.6.0",
    "shadow-cljs": "^2.14.5"      ;; <-- Also, need to have shadow-cljs installed in the client project
  },
  "dependencies": {
    "cypress-clojurescript-preprocessor": "../cypress-clojurescript-preprocessor"  ;;  <-- Directory of the local copy
  }
}

Note the the client project also needs shadow-cljs to be installed (apparently :)).

Then, compile your changes via

$ npm run prepare

And (re)start Cypress in the client project:

$ ./node_modules/.bin/cypress open

Changelog

  • 0.2.0
    • Support REPL in an open test, allows to use the "now" command
    • Finally fix zombie processes after exit, by using signal-exit, thanks to this blog post
    • Detech shadow-cljs startup by polling port/pid files and remove race condition around subprocess stdout/stderr
  • 0.1.7
    • Bump shadow-cljs to 2.14.5
    • Call process/exit to not leave zombies when Cypress exits
    • When adding a new test while the preprocessor is running
      • Use same optimization level as for initial build config
      • Prevent duplicate merge of shadow-cljs-override.edn
  • 0.1.6 Support namespaces with multiple segments (thanks @martinklepsch !), also files with underscore. Bump chokidar to 3.5.2
  • 0.1.5 Merge override config ontop final config, allows to add additional :source-paths
  • 0.1.4 Bundle browserify preprocessor
  • 0.1.3 Add shadow-cljs-override.edn
  • 0.1.2 Bundle Bundle mocha-latte and chai-latte
  • 0.1.0 Initial release