{"id":22474708,"url":"https://github.com/herbsjs/aloe","last_synced_at":"2025-08-30T09:39:10.397Z","repository":{"id":37960230,"uuid":"484218024","full_name":"herbsjs/aloe","owner":"herbsjs","description":"Scenario description and test runner for Herbs","archived":false,"fork":false,"pushed_at":"2023-11-22T17:51:37.000Z","size":788,"stargazers_count":0,"open_issues_count":4,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-15T21:57:06.583Z","etag":null,"topics":["javascript","testing"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/herbsjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-21T22:05:24.000Z","updated_at":"2022-12-03T18:07:30.000Z","dependencies_parsed_at":"2023-11-22T18:40:07.462Z","dependency_job_id":null,"html_url":"https://github.com/herbsjs/aloe","commit_stats":{"total_commits":30,"total_committers":5,"mean_commits":6.0,"dds":0.4666666666666667,"last_synced_commit":"3da7b6570800916de40cc0f09c44b94b4216e34e"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/herbsjs/aloe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Faloe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Faloe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Faloe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Faloe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/herbsjs","download_url":"https://codeload.github.com/herbsjs/aloe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Faloe/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268378965,"owners_count":24240907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","testing"],"created_at":"2024-12-06T13:10:09.129Z","updated_at":"2025-08-02T11:32:14.257Z","avatar_url":"https://github.com/herbsjs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CD Build](https://github.com/herbsjs/aloe/actions/workflows/cd.yml/badge.svg)](https://github.com/herbsjs/aloe/actions/workflows/cd.yml)\n[![codecov](https://codecov.io/gh/herbsjs/aloe/branch/main/graph/badge.svg)](https://codecov.io/gh/herbsjs/aloe)\n[![stability-beta](https://img.shields.io/badge/stability-beta-33bbff.svg)](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#beta)\n\n# Aloe\n\nScenario description and test runner for Herbs\n\n### Installing\n\n```$ npm install @herbsjs/aloe```\n\n### Using\n\n```javascript\nconst { spec, scenario, given, check, samples } = require('@herbsjs/aloe')\n\nconst taskCountSpec = spec({\n    'Change count for the task': scenario({\n        'Given a valid task': given({ task: 'do it', count: 0 }),\n        'When increase count': when((ctx) =\u003e (ctx.count++)),\n        'Must have a increased count': check((ctx) =\u003e { assert.ok(ctx.count === 1) }),\n    }),\n})\n```\n\n## How to use\n\n### Basic Spec\n\nA `spec` represents the primary structure where the scenarios will be placed. It can have one or several scenarios:\n\n```javascript\nconst updateUserSpec = spec({\n    'Update valid User': scenario({ ... }),\n    'Do not update invalid User': scenario({ ... }),\n})\n```\n\nA `scenario` represents a context with input (`given`), action (`when`) and the output validation (`check`). \n\n```javascript\nconst taskCountSpec = spec({\n    'Change count for the task': scenario({\n        'Given a valid task': given({ task: 'do it', count: 0 }),\n        'When increase count': when((ctx) =\u003e (ctx.count++)),\n        'Must have a increased count': check((ctx) =\u003e { assert.ok(ctx.count === 1) }),\n    }),\n})\n```\n\nIt is possible to have many `given` and `check` on the same scenario. \n\n\n\n```javascript\nconst billingSpec = spec({\n    'Calculate a valid bill': scenario({\n        'Given valid items': given({ ... }),\n        'Given a discount': given(async () =\u003e { ... }),\n        'When calculate the bill': when((ctx) =\u003e ...),\n        'Must have a valid bill': check((ctx) =\u003e ...),\n        'Must have a discount': check((ctx) =\u003e ...),\n    }),\n})\n```\nIt's also possible to run `only` a specific scenario.\n\n```javascript\nconst taskCountSpec = spec({\n    'Change count for the task': scenario.only({\n        'Given a valid task': given({ task: 'do it', count: 0 }),\n        'When increase count': when((ctx) =\u003e (ctx.count++)),\n        'Must have a increased count': check((ctx) =\u003e { assert.ok(ctx.count === 1) }),\n    }),\n})\n```\n\n### Use Case Spec\n\nWhen informed a use case the spec will assume all the scenarios are about this use case.\n\nThis will change the behavior of each scenario since it will not necessary to declare a `when`. \n\nIn order to run, the scenario expects a `given` returning a object with `request`, `user` (for use case authentication) and `injection`. \n\nThe result of the use case execution is stored in `ctx.response`.\n\n```javascript\nconst updateUser = require('./updateUser')\n\nconst updateUserSpec = spec({\n\n    usecase: updateUser, \n    \n    'Update a existing user when it is valid': scenario({\n        'Given a valid user': given({\n            request: { id: 1 },\n            user: { can: true },\n            injection: { userRepo: ... },\n        }),\n        // when: default for use case\n        'Must run without errors': check((ctx) =\u003e { assert.ok(ctx.response.isOk) }),\n        'Must confirm update': check((ctx) =\u003e { assert.ok(ctx.response.ok === true) })\n    }),\n})\n```\n\n### Samples\n\nIf instead of validating the scenario with just one input you want to validate the set of inputs it is possible to use `samples`.\n\n```javascript\nconst updateProjectSpec = spec({\n\n    usecase: updateProject,\n    \n    'Update a existing project': scenario({\n        'Projects with due dates': samples([\n            { duedate: ... },               // item 1\n            { duedate: ... },               // item 2\n        ]),\n        'Projects with tasks': samples([\n            { tasks: [] },                  // item 1\n            { tasks: [{ task: ... }] },     // item 2\n        ]),\n        'Given a valid project': given((ctx) =\u003e ({\n            request: ctx.sample,  // each item in `samples()`\n            ...\n        })),\n        'Given a repository with a existing project': given((ctx) =\u003e ... ),\n        'Must run without errors': check((ctx) =\u003e ... ),\n        'Must confirm update': check((ctx) =\u003e ... )\n    })\n}),\n```\n\nIn the above scenario `'Update a existing project'` it will run four times, one for each item in each `samples`. The content of each run is available on `ctx.sample`.\n\n### Context\n\nThe context is created by `samples` or `givens`.\n\n```javascript\nconst updateUserSpec = spec({\n\n    'Update a existing user': scenario({\n        'Given a valid user': given({ name: 'Claudia' }),\n        'Print user name': check((ctx) =\u003e { console.log(ctx.name) }),\n        ...\n```\n\nOr\n\n```javascript\nconst updateUserSpec = spec({\n\n    'Update a existing user': scenario({\n        'Valid users': samples([\n            { name: 'Claudia' },               // item 1\n            { name: 'Claudio' },               // item 2\n        ]),\n        'Given a valid user': given((ctx) =\u003e ctx.sample),\n        'Print user name': check((ctx) =\u003e { console.log(ctx.name) }),\n        ...\n```\n\n### Assertion\n\nTo validate a scenario it is necessary to go through checks with its assertions. If an assertion throws an exception, it is understood that that check failed.\n\nIt is possible to use any assertion library, including native node.js [assertion](https://nodejs.org/api/assert.html#assert).\n\n```javascript\nconst createUserSpec = spec({\n\n    usecase: createUser, \n    \n    'Create a new user': scenario({\n        'Given a valid user': given(...),\n        'Must run without errors': check((ctx) =\u003e { \n            assert.ok(ctx.response.isOk) \n        }),\n        'Must return the created user': check((ctx) =\u003e { \n            assert.ok(ctx.response.ok === aGivenUser()) \n        })\n    }),\n})\n```\n \n### Runner\n\nThe runner is responsible for executing the scenarios and showing the results.\n\n```javascript\nconst { runner } = require('@herbsjs/aloe/runner')\n\n// running a set of pre-loaded specs\nawait runner({ specs })\n\n// or\n\n// load specs from herbarium\nawait runner({ herbarium })\n\n// or\n\n// load specs from the current directory (*.spec.js)\nawait runner() // specsPath can be especified\n```\n\n\n## TODO\n- [ ] doc on web site\n- [ ] CLI: doc `spec` command\n- [ ] skip\n- [X] only\n- [ ] todo / pending\n- [ ] nested scenarios\n- [ ] tests for runner\n- [ ] TAP protocol https://testanything.org/\n\n### Contribute\n\nCome with us to make an awesome *Aloe*.\n\nNow, if you do not have technical knowledge and also have intend to help us, do not feel shy, [click here](https://github.com/herbsjs/aloe/issues) to open an issue and collaborate their ideas, the contribution may be a criticism or a compliment (why not?)\n\nIf you would like to help contribute to this repository, please see [CONTRIBUTING](https://github.com/herbsjs/aloe/blob/master/.github/CONTRIBUTING.md)\n\n### The Herb\n\nThe gel inside of the leaves of the Aloe plant can be used externally to treat minor burns, sun burn, cuts, scrapes and poison ivy. Aloe gel is good for moisturizing the skin and is a main ingredient of many skin care products.\n\nhttps://www.herbslist.net/\n\nhttps://en.wikipedia.org/wiki/Aloe_vera\n\n### License\n\n**Aloe** is released under the\n[MIT license](https://github.com/herbsjs/aloe/blob/master/LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fherbsjs%2Faloe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fherbsjs%2Faloe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fherbsjs%2Faloe/lists"}