57 open-source web apps: how many screens, how many end-to-end spec files
14 of the 57 have no end-to-end spec file in their repository, and 32 of the 40 whose screens could be counted have fewer spec files than screens: the median is 0.25 spec files per screen, about one for every 4 screens.
counted 17 Sep 2026 (14 apps, by hand) and 28 Sep 2026 (43 apps, by script) · each app at the commit in its row · open data, CC BY 4.0
Spec files per screen, app by app #
Each row is one app with both counts: its end-to-end spec files divided by its screens, on a log scale. Left of the solid line, the app has fewer spec files than screens.
40 of 57 apps · spec files ÷ screens · log scale, with the apps that have none on the floor
- Akaunting
none0 / 121 - BookStack
none0 / 93 - FreshRSS
none0 / 63 - Hoppscotch
none0 / 32 - Kanboard
none0 / 102 - Kimai
none0 / 48 - Medusa
none0 / 186 - NocoDB
none0 / 66 - Outline
none0 / 32 - Plane
none0 / 72 - Snipe-IT
none0 / 154 - Weblate
0.0081 / 120 - Linkwarden
0.0281 / 36 - Mealie
0.0302 / 66 - Twenty
0.0489 / 186 - Wagtail
0.0488 / 165 - Redmine
0.1328 / 223 - Umami
0.148 / 59 - Supabase Studio
0.1529 / 190 - Paperless-ngx
0.245 / 21 - PostHog
0.2543 / 169 - Gitea
0.2730 / 111 - InvenTree
0.3026 / 87 - GLPI
0.34107 / 315 - Uptime Kuma
0.477 / 15 - Mastodon
0.50107 / 214 - Formbricks
0.6348 / 76 - Taiga
0.6547 / 72 - Cal.diy
0.6853 / 78 - Baserow
0.6937 / 54 - Keycloak
0.8681 / 94 - Immich
0.8849 / 56 - Documenso
1.15132 / 115 - Vikunja
1.2272 / 59 - Directus
1.3699 / 73 - Discourse
1.78295 / 166 - OpenProject
1.94787 / 406 - Grafana
2.37213 / 90 - n8n
4.66284 / 61 - Appsmith
31.8667 / 21
Not in the picture (17): the apps whose pages are declared in code or generated from records, so there is no page file to count. Their spec files are in the table. Of those, ERPNext, Firefly III, Sylius have no end-to-end spec file either.
Every app, every count #
All 57, with the repository, the commit or tag counted and the date. Sort by any column; the downloads hold the same rows with the rule behind each number.
A zero means the rule found no end-to-end spec file in the repository, not that the project is untested. Many projects test in other ways, and a count of spec files says nothing about a project's quality.
| app | licence | screens | API ops | e2e spec files | per screen | counted at (2026) |
|---|---|---|---|---|---|---|
| Akauntingaccounting · Laravel | BSL-1.1 | 121 | no spec | 0 | 0 | github.com/akaunting/akaunting 0c7792b2e, 27 Sep · script 28 Sep · correct this row |
| Appsmithinternal-tool builder · Java · React | Apache-2.0 | 21 | no spec | 667 | 31.8 | github.com/appsmithorg/appsmith c7e65f8f8, 28 Sep · script 28 Sep · correct this row |
| Baserowno-code database · Django · Nuxt | MIT + premium | 54 | no spec | 37 | 0.69 | gitlab.com/baserow/baserow default branch · by hand 17 Sep · correct this row |
| BookStackwiki · PHP · Laravel | MIT | 93 | 77 | 0 | 0 | codeberg.org/bookstack/bookstack v26.05.5, 14 Sep · by hand 17 Sep · correct this row |
| Cal.diyscheduling · Next.js | MIT | 78 | 121 | 53 | 0.68 | github.com/calcom/cal.diy default branch · by hand 17 Sep · correct this row |
| Chatwootcustomer support · Rails · Vue | MIT + enterprise | no count | 154 | 3 | github.com/chatwoot/chatwoot default branch · by hand 17 Sep · correct this row | |
| Directusdata platform · Node · Vue | MSCL-1.0 (source-available) | 73 | 136 | 99 | 1.36 | github.com/directus/directus 1694b1904, 25 Sep · script 28 Sep · correct this row |
| Discourseforum · Rails · Ember | GPL-2.0 | 166 | no spec | 295 | 1.78 | github.com/discourse/discourse 3b6713ef7, 28 Sep · script 28 Sep · correct this row |
| Documensodocument signing · React Router 7 | AGPL-3.0 + ee | 115 | no spec | 132 | 1.15 | github.com/documenso/documenso default branch · by hand 17 Sep · correct this row |
| ERPNextERP · Frappe (Python) | GPL-3.0 | no count | no spec | 0 | github.com/frappe/erpnext 89f490f72, 28 Sep · script 28 Sep · correct this row | |
| Firefly IIIpersonal finance · PHP | AGPL-3.0 | no count | no spec | 0 | github.com/firefly-iii/firefly-iii default branch · by hand 17 Sep · correct this row | |
| Formbrickssurveys · Next.js | AGPL-3.0 + ee | 76 | 72 | 48 | 0.63 | github.com/formbricks/formbricks default branch · by hand 17 Sep · correct this row |
| FreshRSSfeed reader · PHP | AGPL-3.0 | 63 | no spec | 0 | 0 | github.com/FreshRSS/FreshRSS a8e5e6300, 28 Sep · script 28 Sep · correct this row |
| Ghostpublishing · Node · Ember/React | MIT | no count | no spec | 101 | github.com/TryGhost/Ghost 7c3067680, 28 Sep · script 28 Sep · correct this row | |
| Giteacode hosting · Go templates | MIT | 111 | 537 | 30 | 0.27 | github.com/go-gitea/gitea fba8d7eac, 28 Sep · script 28 Sep · correct this row |
| GLPIIT service desk · PHP · Twig | GPL-3.0 | 315 | no spec | 107 | 0.34 | github.com/glpi-project/glpi af37be7a7, 28 Sep · script 28 Sep · correct this row |
| Grafanadashboards · Go · React | AGPL-3.0 | 90 | 314 | 213 | 2.37 | github.com/grafana/grafana 5f4490098, 28 Sep · script 28 Sep · correct this row |
| HoppscotchAPI client · Vue · NestJS | MIT | 32 | no spec | 0 | 0 | github.com/hoppscotch/hoppscotch a9ffa29ce, 23 Sep · script 28 Sep · correct this row |
| Immichphotos · NestJS · SvelteKit | AGPL-3.0 | 56 | 276 | 49 | 0.88 | github.com/immich-app/immich default branch · by hand 17 Sep · correct this row |
| InvenTreeinventory · Django · React | MIT | 87 | 670 | 26 | 0.30 | github.com/inventree/InvenTree default branch · by hand 17 Sep · correct this row |
| Kanboardkanban · PHP | MIT | 102 | no spec | 0 | 0 | github.com/kanboard/kanboard cf53b5459, 11 Sep · script 28 Sep · correct this row |
| Keycloakidentity and access · Java · React | Apache-2.0 | 94 | no spec | 81 | 0.86 | github.com/keycloak/keycloak 9f8eb2d23, 28 Sep · script 28 Sep · correct this row |
| Kimaitime tracking · Symfony · Twig | AGPL-3.0 | 48 | no spec | 0 | 0 | github.com/kimai/kimai 8528bb174, 27 Sep · script 28 Sep · correct this row |
| Linkwardenbookmarks · Next.js | AGPL-3.0 | 36 | no spec | 1 | 0.028 | github.com/linkwarden/linkwarden 952ac4540, 9 Sep · script 28 Sep · correct this row |
| Mastodonsocial network · Rails · React | AGPL-3.0 | 214 | no spec | 107 | 0.50 | github.com/mastodon/mastodon 35c345010, 28 Sep · script 28 Sep · correct this row |
| Mattermostteam chat · Go · React | Apache-2.0 + ee | no count | 605 | 996 | github.com/mattermost/mattermost 05553d083, 28 Sep · script 28 Sep · correct this row | |
| Mealierecipes · FastAPI · Nuxt | AGPL-3.0 | 66 | no spec | 2 | 0.030 | github.com/mealie-recipes/mealie 86aec702a, 28 Sep · script 28 Sep · correct this row |
| Medusacommerce · Node · React | MIT + ee | 186 | 486 | 0 | 0 | github.com/medusajs/medusa default branch · by hand 17 Sep · correct this row |
| Metabaseanalytics · Clojure · React | AGPL-3.0 + ee | no count | 637 | 427 | github.com/metabase/metabase 1a0ff4340, 28 Sep · script 28 Sep · correct this row | |
| n8nworkflow automation · Node · Vue | Sustainable Use (fair-code) + ee | 61 | 32 | 284 | 4.66 | github.com/n8n-io/n8n 41dbfd987, 28 Sep · script 28 Sep · correct this row |
| Nextcloud serverfile sync and share · PHP · Vue | AGPL-3.0 | no count | 301 | 102 | github.com/nextcloud/server c41a9138d, 28 Sep · script 28 Sep · correct this row | |
| NocoDBno-code database · Node · Nuxt | Sustainable Use (fair-code) | 66 | 278 | 0 | 0 | github.com/nocodb/nocodb 8cfa45dcb, 28 Sep · script 28 Sep · correct this row |
| Odoobusiness suite · Python · OWL | LGPL-3.0 | no count | no spec | 525 | github.com/odoo/odoo a240a91eb, 28 Sep · script 28 Sep · correct this row | |
| OpenProjectproject management · Rails · Angular | GPL-3.0 | 406 | 331 | 787 | 1.94 | github.com/opf/openproject c0146dbdd, 28 Sep · script 28 Sep · correct this row |
| Outlineteam wiki · Node · React | BSL-1.1 | 32 | no spec | 0 | 0 | github.com/outline/outline 179d7dd7b, 27 Sep · script 28 Sep · correct this row |
| Paperless-ngxdocuments · Django · Angular | GPL-3.0 | 21 | no spec | 5 | 0.24 | github.com/paperless-ngx/paperless-ngx 88000aab4, 28 Sep · script 28 Sep · correct this row |
| Penpotdesign tool · Clojure | MPL-2.0 | no count | no spec | 65 | github.com/penpot/penpot f9b8f1ba7, 28 Sep · script 28 Sep · correct this row | |
| Planeproject tracking · Django · Next.js | AGPL-3.0 | 72 | no spec | 0 | 0 | github.com/makeplane/plane default branch · by hand 17 Sep · correct this row |
| Plausibleweb analytics · Elixir · React | AGPL-3.0 | no count | no spec | 12 | github.com/plausible/analytics ee3a27bb3, 28 Sep · script 28 Sep · correct this row | |
| PostHogproduct analytics · Django · React | MIT + ee | 169 | no spec | 43 | 0.25 | github.com/PostHog/posthog cb4522375, 28 Sep · script 28 Sep · correct this row |
| Redmineproject tracking · Rails | GPL-2.0+ | 223 | no spec | 28 | 0.13 | github.com/redmine/redmine c9008f4ce, 28 Sep · script 28 Sep · correct this row |
| Rocket.Chatteam chat · Meteor · React | MIT + ee | no count | no spec | 280 | github.com/RocketChat/Rocket.Chat b149e10ee, 28 Sep · script 28 Sep · correct this row | |
| Saleor dashboardcommerce admin · React (GraphQL API) | BSD | no count | no spec | 40 | github.com/saleor/saleor-dashboard 8d678ea21, 28 Sep · script 28 Sep · correct this row | |
| Snipe-ITasset management · Laravel | AGPL-3.0 | 154 | no spec | 0 | 0 | github.com/grokability/snipe-it d5bf95c02, 26 Sep · script 28 Sep · correct this row |
| Strapiheadless CMS · Node · React | MIT + ee | no count | no spec | 100 | github.com/strapi/strapi bba9685b9, 28 Sep · script 28 Sep · correct this row | |
| Supabase Studiodatabase dashboard · Next.js | Apache-2.0 | 190 | 170 | 29 | 0.15 | github.com/supabase/supabase 8e2071247, 28 Sep · script 28 Sep · correct this row |
| Syliuscommerce · PHP | MIT | no count | no spec | 0 | github.com/Sylius/Sylius default branch · by hand 17 Sep · correct this row | |
| Taigaagile boards · Django · AngularJS | AGPL-3.0 front · MPL-2.0 back | 72 | no spec | 47 | 0.65 | github.com/taigaio/taiga-front d2da06116, 4 Sep · script 28 Sep · correct this row |
| TwentyCRM · NestJS · React | AGPL-3.0 + ee | 186 | no spec | 9 | 0.048 | github.com/twentyhq/twenty default branch · by hand 17 Sep · correct this row |
| Umamiweb analytics · Next.js | MIT | 59 | no spec | 8 | 0.14 | github.com/umami-software/umami ec0ff5038, 16 Sep · script 28 Sep · correct this row |
| Uptime Kumauptime monitor · Node · Vue | MIT | 15 | no spec | 7 | 0.47 | github.com/louislam/uptime-kuma b6adc19d0, 23 Sep · script 28 Sep · correct this row |
| Vikunjatasks · Go · Vue | AGPL-3.0 | 59 | 172 | 72 | 1.22 | github.com/go-vikunja/vikunja 18f3572ed, 28 Sep · script 28 Sep · correct this row |
| WagtailCMS · Django | BSD | 165 | no spec | 8 | 0.048 | github.com/wagtail/wagtail e57ccdbc0, 25 Sep · script 28 Sep · correct this row |
| Weblatetranslation · Django | GPL-3.0 | 120 | 202 | 1 | 0.008 | github.com/WeblateOrg/weblate 6ff77533c, 27 Sep · script 28 Sep · correct this row |
| Wiki.jswiki · Node · Vue | AGPL-3.0 | no count | no spec | 1 | github.com/requarks/wiki 712a3a5bb, 22 Sep · script 28 Sep · correct this row | |
| Zammadhelpdesk · Rails · Vue | AGPL-3.0 | no count | no spec | 286 | github.com/zammad/zammad 619ef87bb, 28 Sep · script 28 Sep · correct this row | |
| Zulipteam chat · Django · Tornado | Apache-2.0 | no count | no spec | 20 | github.com/zulip/zulip default branch · by hand 17 Sep · correct this row |
If a row is wrong, write to hello@featkpr.com with the app and the column; we correct it and date the fix on this page. How corrections work.
"no count": the screens cannot be counted from the files, or no API spec is committed; hover for the reason, and the downloads carry it. "by hand": report T2, 17 Sep 2026. "script": tools/library_count.py, 28 Sep 2026.
How we counted #
The protocol of our research report T2 (17 Sep 2026), made mechanical on 28 Sep 2026 in one script. Every number is a count of files in the repository, at the commit shown, with the rule that produced it.
- A screen
- a page a person can land on: one page file or one route declaration, never a component. In file-routed front ends (Next.js, SvelteKit, Nuxt, React Router route modules, a Vue app's views), page files minus layouts, API routes and _app/_document. Where the router is code (React Router, Vue Router, Angular, Ember, AngularJS), the route declarations in the router file the rule names. In server-rendered apps (Rails, Django, Laravel, Twig, Go templates, plain PHP), the templates that render a whole page: ones that extend a layout, or a Rails view that is not a partial, a layout or a mail.
- No screen count
- an app whose pages cannot be counted either way from its files (routes spread across the code, or screens generated from records) gets none, with the reason, rather than a guess.
- An API operation
- a (path, method) pair in the OpenAPI or Swagger spec committed to the repository, following a spec split into files through its $refs. No committed spec (GraphQL, RPC, a spec generated only at runtime or kept in another repository): no count.
- An end-to-end spec file
- a file of the suite the project runs against the running app from outside, through a browser (Playwright, Cypress, Puppeteer, Selenium, Protractor, Capybara system specs, Odoo tours) or over HTTP, in the directory the project names for it. Unit, component and in-process HTTP tests are not counted.
Spec files are not tests. One spec file can hold one test or fifty. We count files because a file is what the rule can see without running anything.
Screens are not features. A screen is a page file. One screen can carry many features, and one feature can span many screens.
Two counts: report T2 counted 14 apps by hand on 17 Sep 2026 (BookStack at its v26.05.5 tag, the others at their default branch that day, with no commit recorded). On 28 Sep 2026 the script counted 43 more at each repository's default branch, and recounted 4 of T2's apps to check the rules against T2's figures.
Where both counts exist #
T2's hand count (17 Sep) beside the script's (28 Sep). The table and the picture use T2's; the downloads carry both.
| app | count | T2, 17 Sep 2026 | script, 28 Sep 2026 |
|---|---|---|---|
Documensoa1d4bec14 | screens | 115 | 112 differs |
| API operations | no count | no count | |
| e2e spec files | 132 | 135 differs | |
Formbricks43db6ad83 | screens | 76 | 78 differs |
| API operations | 72 | 72 | |
| e2e spec files | 48 | 49 differs | |
Immichaa0233784 | screens | 56 | 56 |
| API operations | 276 | 276 | |
| e2e spec files | 49 | 49 | |
InvenTreec2b7f71f2 | screens | 87 | 87 |
| API operations | 670 | no count differs | |
| e2e spec files | 26 | 26 |
InvenTree's API count differs because its spec is kept in another repository (inventree/schema): T2 read it there, and the script reads only the repository it counts.
What this does not show #
- Not a sample of all open-source apps. The apps were picked as candidates for featkpr to map, plus well-known self-hosted apps across stacks. Nothing here says what share of all projects have end-to-end suites.
- Not a measure of quality. Fewer spec files than screens does not mean a screen is untested: unit and integration tests are not counted, and one spec file can cover many screens.
- Rules differ by stack. A screen in a Rails app (a template) and in a React app (a route declaration) are counted by different rules, so compare apps within a stack with more care than across stacks.
- One moment in time. Each count is at one commit. Repositories move every day; run the rule again and the number may differ.
- Rules, not a reading of every file. Each number is what its rule matched, and the 4 recounted apps are checked against T2's hand counts. A rule can still count a file it should not, or miss one.
How to count it again #
Every row names the repository, the commit and the rule, so any one number can be checked with a clone and a file listing.
- Clone the repository and check out the commit in the row.
- List the files: git ls-tree -r --name-only HEAD.
- Apply the row's rule: the files whose path matches the pattern, minus the exclusion, or the files whose text matches it. The count is the number of files (or of route declarations, where the rule says occurrences).
The script that did it, tools/library_count.py (Python, no dependencies beyond an optional YAML reader), shallow-clones each repository with no blobs, fetches only the files a rule must read and prints one JSON record per app. Its output of 28 Sep 2026 is the receipt the site reads. Run today, it counts today's default branch, so the numbers move with the repositories.
receipt: research/reports/library-count-2026-09-28.json · 47 records, one per repository counted on 28 Sep 2026
A neighbour dataset: E2EGit #
The closest published dataset we know of, and how this one differs.
University of Naples Federico II and Vrije Universiteit Brussel · 2025 IEEE/ACM 22nd International Conference on Mining Software Repositories (MSR), pp. 836-840 · read 28 Sep 2026
E2EGit: A Dataset of End-to-End Web Tests in Open Source Projects (Sergio Di Meglio, Luigi Libero Lucio Starace, Valeria Pontillo, Ruben Opdebeeck, Coen De Roover, Sergio Di Martino) publishes a dataset of GitHub repositories written in Java, JavaScript, TypeScript or Python that depend on a web framework. It finds Selenium, Playwright, Puppeteer and Cypress tests (and JMeter and Locust performance tests) and counts test cases per file.
| aspect | E2EGit | this count |
|---|---|---|
| What is counted | test cases, inside each test file | end-to-end spec files, not the tests inside them |
| Which projects | 5,563 web repositories (at least 2,000 commits, 10 contributors and 100 stars, not a fork); the 472 with web GUI tests are listed | 57 apps, chosen one by one; the 14 with no end-to-end spec file are in the data |
| The app's size | commits, contributors, lines of code and stars, not its pages | screens, and API operations where a spec is committed |
| What it found | 43,670 web GUI test cases in 472 repositories, and 271 performance tests in 84 | 6,233 end-to-end spec files in 43 apps |
| When | mined May 2024; published 26 Nov 2024 | counted 17 Sep 2026 and 28 Sep 2026 |
| Licence | CC BY 4.0, on Zenodo | CC BY 4.0, on this page |
Paper: doi 10.1109/MSR66628.2025.00121 (the authors' PDF). Data: Zenodo, doi 10.5281/zenodo.14221860, CC BY 4.0. Every E2EGit figure above is from the paper, read 28 Sep 2026.
Download and cite #
The same rows as the table, with the rule behind every number, generated from the site's data when the site is built.
- open-source-apps-counted-2026-09-28.csvUTF-8, one row per app. The first lines start with # and name the licence, the source and the dates: skip them as comments (pandas: comment="#").
- open-source-apps-counted-2026-09-28.jsonThe rows, a dictionary of every column, the summary figures on this page and the provenance.
Licence: Creative Commons Attribution 4.0 International (CC BY 4.0). Use it for anything, including commercially; name featkpr as the source and link to this page.
Cite this #
featkpr, 57 open-source web apps counted, 28 Sep 2026, CC BY 4.0, https://featkpr.com/library/counted
BibTeX
@misc{featkpr_counted_20260928,
author = {{featkpr}},
title = {57 open-source web apps counted: screens, API operations and end-to-end spec files},
year = {2026},
month = {sep},
howpublished = {\url{https://featkpr.com/library/counted}},
note = {Dataset, CC BY 4.0. Counted 17 Sep 2026 and 28 Sep 2026}
}See featkpr map an app #
We counted these apps to choose which ones featkpr maps first. featkpr maps a web app's features, the flows people take through them and the goals they reach, and tests sit on top of that map. The counts are published as they are, so anyone can check them, reuse them or count more.