# OpenEMR's npm test exits with a failure, and its browser targets reach IE 8

> An electronic health records application whose README is an index of five other documents and a row of fourteen workflow badges, whose documented build starts by excluding development dependencies, and whose JavaScript manifest declares Internet Explorer 8 support alongside a Node 24 requirement.

**openemr/openemr** — The most popular open source electronic health records and medical practice management solution.

- Repository: https://github.com/openemr/openemr
- Website: https://open-emr.org/
- Stars: 5,539 · Forks: 3,097
- Language: PHP
- License: GPL-3.0
- Published: 2026-08-27 · Updated: 2026-08-27 · Language: en
- Canonical page: https://hysenlabs.com/projects/openemr-openemr

## npm test is a placeholder that always fails

The interface package manifest contains the usual generated stub where a test command should be, printing an error and exiting non-zero. Anyone who runs the conventional command gets a failure that says nothing about their code.

```
"test:js": "jest",
"stylelint": "npx stylelint '**/*.{css,scss}'",
```

The real tests are a script further down, run by jest, with a coverage variant beside it, and the repository carries a workflow badge dedicated to JavaScript tests, so the tests do run somewhere. Two smaller signals in the same block: the styling check resolves its binary through npx while every other script calls a local one, and the lint and format commands for styles are wired as a fix variant of the check rather than as a separate formatter. The practical effect is that the entry point a newcomer tries first is broken, and the entry points that work are discoverable only by reading the manifest.

## The browser list includes IE 8 while the build requires Node 24

The same manifest carries two lists that disagree about the era. The runtime targets are conservative to the point of being a policy statement.

```
"ie >= 8",
"safari >= 7",
"bb >= 10",
```

Alongside a share-of-market entry there are minimum versions for Edge, mobile IE, Firefox, Chrome, Safari, Opera, iOS, Android and BlackBerry, and the oldest of those is Internet Explorer 8. The toolchain, by contrast, is pinned to the present: the engines field requires Node 24 or newer, the README states the same requirement before the build commands, and the lint and bundler dependencies are on current major versions with a flat configuration file. Both can be true at once, since an old browser target costs nothing at build time, but it does mean the compiled output is written for a much wider audience than the machine producing it.

## The documented build starts by excluding development dependencies

Building from the repository is four commands long.

```
composer install --no-dev
npm install
npm run build
composer dump-autoload -o
```

The first flag is the interesting one. Excluding development dependencies is a sensible default for a deployment, and it is what the front-end build wants, but the repository also ships the configuration for a static analyser, a dependency allow-list checker and a rules-driven refactoring tool, each with its own workflow badge in the README. None of those can run from the path above. The JavaScript half is separate anyway, since the bundler is a development dependency of the Node manifest and is installed by the npm commands, and the production bundle is a webpack build followed by a script that synchronises the compiled stylesheets.

## Two example configuration files, and no real ones

The tree holds example files instead of live configuration, which is the right default for something that ships into hospital networks, and there are two of them. One is the environment file template, which defines exactly two settings.

```
OPENEMR__ENVIRONMENT=
OPENEMR__NO_BACKGROUND_TASKS=
```

The first selects a production or development mode and the comment is emphatic that no spaces or quotes are allowed. The second is the interesting one: it disables periodic background service execution, described in the file as Ajax piggybacking, and the comment is careful to say that session timeouts and notification badges stay active regardless, which tells you those two features are not implemented on top of the background task system. It accepts true, 1, false or 0. The other example file is an Apache configuration template, and beside them sit an Ant build file, a bare cli entry, a version marker and a submodules declaration.

## Development dependencies are exact pins, and one crosses a major version

The dependency list in the interface manifest has no ranges at all. Every entry carries an exact version, from the CSS tooling through the bundler plugins to the linter and the test runner, which makes builds reproducible and makes every bump an explicit commit. Within that discipline one pair does not line up. The test runner is pinned in the 29 series and its type definitions match, but the environment package it needs is pinned in the 30 series, a major version ahead of the runner that consumes it. The same manifest runs a package fetcher as its install step, before the script that installs assets, and that fetcher works by pulling archives from source repositories rather than from the package registry, so a plain install of this project reaches outside the registry twice before it starts building anything.

## The README is an index of five documents and fourteen badges

What the front page contains is mostly pointers. Bugs go to the issue tracker, general questions to the forum or the chat, security reports to a policy file under the workflows directory, and the API, Docker and FHIR integration each have their own readme in the repository root. Support is described with a link to a guide on the project wiki. The CI story is told entirely in badges: syntax, styling, tests, JavaScript tests, static analysis, the refactoring tool, shell checking, two Docker linting jobs, an isolated test job, an interoperability test job, two Composer jobs, an API docs job and a coverage service, which is fourteen badges and no prose about what any of them gate. Two files in the tree are not mentioned there at all, an isolated testing readme and an assistant instruction file, which is roughly the ratio of what a newcomer needs to what the front page offers.

## Tag names use underscores, the release titles use dots

The release naming is inconsistent in a way that matters if you script against it. The tags are written with underscores, and the newest three are a patch, a minor and a minor in that order of recency, arriving inside a single month. The release titles spell the same versions with dots. So a tag and its own release name are two different strings for one build, which is the sort of thing that breaks a script matching one against the other. The sponsorship note is more revealing about the project's shape: the sponsors are credited as the major sponsors of ONC certification, linking to a wiki page on Stage III meaningful use, which places the project's incentive in regulatory certification rather than in feature work.

## Conclusion

OpenEMR is a full clinical application rather than a library, so the useful question is whether its documented build path and its interface package hold up in your environment. Two things to check before starting. The from-source instructions begin by excluding development dependencies, so the static analysis and dependency checks the project runs in its own workflows are not part of that path, and the JavaScript side has to be built separately through npm. And the package manifest is internally inconsistent in two places, with a test entry that always fails and a jest environment pinned a major version ahead of jest itself. Treat the README as a table of contents, since the substance sits in the API, Docker and FHIR documents it points to.

## FAQ

### What is OpenEMR used for?

It is an electronic health records and medical practice management application. The README lists fully integrated records, practice management, scheduling, electronic billing and internationalization, and says it runs on Windows, Linux, Mac OS X and many other platforms. Its API, Docker setup and FHIR integration each have a separate readme in the repository root.

### Is OpenEMR HIPAA compliant?

The repository makes no compliance claim, so nothing here confirms or denies it. What it does point to is a security policy file for reporting vulnerabilities, a support guide on the project wiki, and a licence file for the GNU GPL. Any compliance answer has to come from your own deployment and its documentation, not from this repository.

### How do I build OpenEMR from the code repository?

With Node.js 24 or newer, run composer install --no-dev, then npm install, npm run build and composer dump-autoload -o. Note that the first flag excludes PHP development dependencies, so the static analysis and dependency checks the project's own workflows run are not installed by that path, and the JavaScript bundle is built by the separate npm commands.

### Does npm test run the JavaScript tests in OpenEMR?

No. The test entry in the interface package manifest is a generated placeholder that prints an error and exits with a failure. The real tests are run by jest under a separate script, with a coverage variant next to it, and the lint and style checks have their own scripts as well.

### What can the environment settings in OpenEMR turn off?

The example environment file defines two. One selects a production or development mode, with no spaces or quotes allowed. The other disables periodic background service execution, described as Ajax piggybacking, and states that session timeouts and notification badges remain active either way. It accepts true, 1, false or 0.

## Sources

- [Official documentation](https://open-emr.org/)
- [Official README](https://github.com/openemr/openemr#readme)
- [Project repository](https://github.com/openemr/openemr)
- [Release notes](https://github.com/openemr/openemr/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/openemr-openemr
