|
| 1 | +<p align="center"> |
| 2 | + <a href="https://github.com/ipfs/helia" title="Helia"> |
| 3 | + <img src="https://raw.githubusercontent.com/ipfs/helia/main/assets/helia.png" alt="Helia logo" width="300" /> |
| 4 | + </a> |
| 5 | +</p> |
| 6 | + |
| 7 | +<h3 align="center"><b>Test Helia with Jest and TypeScript</b></h3> |
| 8 | + |
| 9 | +<p align="center"> |
| 10 | + <img src="https://raw.githubusercontent.com/jlord/forkngo/gh-pages/badges/cobalt.png" width="200"> |
| 11 | + <br> |
| 12 | + <a href="https://ipfs.github.io/helia/modules/helia.html">Explore the docs</a> |
| 13 | + · |
| 14 | + <a href="https://github.com/ipfs-examples/helia-examples/issues">Report Bug</a> |
| 15 | + · |
| 16 | + <a href="https://github.com/ipfs-examples/helia-examples/issues">Request Feature/Example</a> |
| 17 | +</p> |
| 18 | + |
| 19 | +## About |
| 20 | + |
| 21 | +[Jest](https://jestjs.io/) is a JavaScript testing framework that transpiles code on the fly to enable a fast-feedback testing cycle for the developer. |
| 22 | + |
| 23 | +It is typically used to test browser code, whereby it injects an artificial DOM implementation into the global scope to enable running the actual tests on [Node.js](https://nodejs.org) to take advantage of faster startup times at the cost of not always having 100% feature/behavior parity with the actual target platform. |
| 24 | + |
| 25 | +In some circumstances this is helpful but it can also result in hard to debug errors, particularly around resolving modules and loading them. |
| 26 | + |
| 27 | +Some developers also use Jest to test pure Node.js code which the benefit of familiarity but also has the same issues around transpilation. |
| 28 | + |
| 29 | +Adding [TypeScript](https://www.typescriptlang.org/) into the mix adds another layer of misdirection so the error messages are more cryptic than just running plain JS. |
| 30 | + |
| 31 | +``` |
| 32 | +Cannot find module 'helia' from '../src/index.ts' |
| 33 | +
|
| 34 | +Require stack: |
| 35 | + /path/to/project/src/index.ts |
| 36 | + index.spec.ts |
| 37 | +
|
| 38 | +> 1 | import { createHelia } from 'helia' |
| 39 | + 2 | import type { Helia } from '@helia/interface' |
| 40 | +``` |
| 41 | + |
| 42 | +In this example we are going to cover the config necessary to use Jest with TypeScript that compiles to ESM modules. |
| 43 | + |
| 44 | +### 1. TypeScript and ESM |
| 45 | + |
| 46 | +Please see the [helia-typescript](https://github.com/ipfs-examples/helia-typescript) example to ensure your application is building your TypeScript source to ESM correctly. |
| 47 | + |
| 48 | +At the very minimum you should check the compiled output of your application to ensure no calls to `require` are present and instead all modules are loaded via `import`. |
| 49 | + |
| 50 | +### 2. `ts-jest` Transform |
| 51 | + |
| 52 | +[ts-jest](https://www.npmjs.com/package/ts-jest) is a Jest transformer for your `.ts` source files that are under test. |
| 53 | + |
| 54 | +By default it will transpile code to [CommonJS](https://en.wikipedia.org/wiki/CommonJS) which is undesirable if your codebase is designed to be consumed as ESM. |
| 55 | + |
| 56 | +To get it to transform code to ESM, the [useESM](https://kulshekhar.github.io/ts-jest/docs/next/getting-started/options/useESM/) option must be set: |
| 57 | + |
| 58 | +**jest.config.json** |
| 59 | + |
| 60 | +```json |
| 61 | +{ |
| 62 | + "transform": { |
| 63 | + "^.+\\.(t|j)s$": ["ts-jest", { |
| 64 | + "useESM": true |
| 65 | + }] |
| 66 | + } |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +### 3. `ts-jest` Presets |
| 71 | + |
| 72 | +By default `ts-jest` will not be able to load ES Modules: |
| 73 | + |
| 74 | +``` |
| 75 | +FAIL test/index.spec.ts |
| 76 | + ● Test suite failed to run |
| 77 | +
|
| 78 | + Cannot find module 'helia' from '../src/index.ts' |
| 79 | +
|
| 80 | + Require stack: |
| 81 | + /Users/alex/Documents/Workspaces/ipfs-examples/helia-examples/examples/helia-jest-typescript/src/index.ts |
| 82 | + index.spec.ts |
| 83 | +
|
| 84 | + > 1 | import { createHelia } from 'helia' |
| 85 | + | ^ |
| 86 | + 2 | import type { Helia } from '@helia/interface' |
| 87 | +``` |
| 88 | + |
| 89 | +To fix this it is necessary to tell Jest to use the `ts-jest` `default-esm` preset: |
| 90 | + |
| 91 | +**jest.config.json** |
| 92 | + |
| 93 | +```json |
| 94 | +{ |
| 95 | + "preset": "ts-jest/presets/default-esm" |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +### 4. Importing other files from the same project |
| 100 | + |
| 101 | +When authoring ESM, [file extensions are mandatory](https://nodejs.org/api/esm.html#esm_mandatory_file_extensions), however `ts-jest` will not resolve (for example) `./index.ts` when importing `./index.js`: |
| 102 | + |
| 103 | +``` |
| 104 | +Cannot find module '../src/index.js' from 'index.spec.ts' |
| 105 | +
|
| 106 | + at Resolver._throwModNotFoundError (../../../node_modules/jest-resolve/build/resolver.js:427:11) |
| 107 | +``` |
| 108 | + |
| 109 | +The solution is to use a module name mapper to remove the file extension from relative imports and then Jest can fall back to it's version of the Node.js [require.resolve algorithm](https://nodejs.org/api/modules.html#all-together) which will locate `index.ts` from an `index.js` import: |
| 110 | + |
| 111 | +**jest.config.json** |
| 112 | + |
| 113 | +```json |
| 114 | +{ |
| 115 | + "moduleNameMapper": { |
| 116 | + "^(\\.{1,2}/.*)\\.[jt]s$": "$1" |
| 117 | + } |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +## Putting it all together |
| 122 | + |
| 123 | +Your `jest.config.json` should look something like this: |
| 124 | + |
| 125 | +```json |
| 126 | +{ |
| 127 | + "moduleFileExtensions": [ |
| 128 | + "js", |
| 129 | + "json", |
| 130 | + "ts" |
| 131 | + ], |
| 132 | + "rootDir": "test", |
| 133 | + "testRegex": ".*\\.spec\\.ts$", |
| 134 | + "transform": { |
| 135 | + "^.+\\.(t|j)s$": ["ts-jest", { |
| 136 | + "useESM": true |
| 137 | + }] |
| 138 | + }, |
| 139 | + "preset": "ts-jest/presets/default-esm", |
| 140 | + "moduleNameMapper": { |
| 141 | + "^(\\.{1,2}/.*)\\.[jt]s$": "$1" |
| 142 | + } |
| 143 | +} |
| 144 | +``` |
| 145 | + |
| 146 | +We can now run the tests: |
| 147 | + |
| 148 | +```console |
| 149 | +% NODE_OPTIONS=--experimental-vm-modules jest |
| 150 | + |
| 151 | + PASS test/index.spec.ts (6.055 s) |
| 152 | + Helia |
| 153 | + libp2p |
| 154 | + ✓ should have a peer id (519 ms) |
| 155 | + |
| 156 | +Test Suites: 1 passed, 1 total |
| 157 | +Tests: 1 passed, 1 total |
| 158 | +Snapshots: 0 total |
| 159 | +Time: 6.134 s |
| 160 | +Ran all test suites. |
| 161 | +``` |
| 162 | + |
| 163 | +That's it! You just successfully ran a suite that can test your Helia application. |
| 164 | + |
| 165 | +## About The Project |
| 166 | + |
| 167 | +- Read the [docs](https://ipfs.github.io/helia/modules/helia.html) |
| 168 | +- Look into other [examples](https://github.com/ipfs-examples/helia-examples) to learn how to spawn a Helia node in Node.js and in the Browser |
| 169 | +- Visit https://dweb-primer.ipfs.io to learn about IPFS and the concepts that underpin it |
| 170 | +- Head over to https://proto.school to take interactive tutorials that cover core IPFS APIs |
| 171 | +- Check out https://docs.ipfs.io for tips, how-tos and more |
| 172 | +- See https://blog.ipfs.io for news and more |
| 173 | +- Need help? Please ask 'How do I?' questions on https://discuss.ipfs.io |
| 174 | + |
| 175 | +## Getting Started |
| 176 | + |
| 177 | +### Prerequisites |
| 178 | + |
| 179 | +Make sure you have installed all of the following prerequisites on your development machine: |
| 180 | + |
| 181 | +- Git - [Download & Install Git](https://git-scm.com/downloads). OSX and Linux machines typically have this already installed. |
| 182 | +- Node.js - [Download & Install Node.js](https://nodejs.org/en/download/) and the npm package manager. |
| 183 | + |
| 184 | +### Installation and Running example |
| 185 | + |
| 186 | +```console |
| 187 | +> npm install |
| 188 | +> npm test |
| 189 | +``` |
| 190 | + |
| 191 | +## Documentation |
| 192 | + |
| 193 | +- [IPFS Primer](https://dweb-primer.ipfs.io/) |
| 194 | +- [IPFS Docs](https://docs.ipfs.io/) |
| 195 | +- [Tutorials](https://proto.school) |
| 196 | +- [More examples](https://github.com/ipfs-examples/helia-examples) |
| 197 | +- [API - Helia](https://ipfs.github.io/helia/modules/helia.html) |
| 198 | +- [API - @helia/unixfs](https://ipfs.github.io/helia-unixfs/modules/helia.html) |
| 199 | + |
| 200 | +## Contributing |
| 201 | + |
| 202 | +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. |
| 203 | + |
| 204 | +1. Fork the IPFS Project |
| 205 | +2. Create your Feature Branch (`git checkout -b feature/amazing-feature`) |
| 206 | +3. Commit your Changes (`git commit -a -m 'feat: add some amazing feature'`) |
| 207 | +4. Push to the Branch (`git push origin feature/amazing-feature`) |
| 208 | +5. Open a Pull Request |
| 209 | + |
| 210 | +## Want to hack on IPFS? |
| 211 | + |
| 212 | +[](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) |
| 213 | + |
| 214 | +The IPFS implementation in JavaScript needs your help! There are a few things you can do right now to help out: |
| 215 | + |
| 216 | +Read the [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md) and [JavaScript Contributing Guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md). |
| 217 | + |
| 218 | +- **Check out existing issues** The [issue list](https://github.com/ipfs/helia/issues) has many that are marked as ['help wanted'](https://github.com/ipfs/helia/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22) or ['difficulty:easy'](https://github.com/ipfs/helia/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Adifficulty%3Aeasy) which make great starting points for development, many of which can be tackled with no prior IPFS knowledge |
| 219 | +- **Look at the [Helia Roadmap](https://github.com/ipfs/helia/blob/main/ROADMAP.md)** This are the high priority items being worked on right now |
| 220 | +- **Perform code reviews** More eyes will help |
| 221 | + a. speed the project along |
| 222 | + b. ensure quality, and |
| 223 | + c. reduce possible future bugs |
| 224 | +- **Add tests**. There can never be enough tests |
| 225 | + |
| 226 | +[cid]: https://docs.ipfs.tech/concepts/content-addressing "Content Identifier" |
| 227 | +[Uint8Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array |
| 228 | +[libp2p]: https://libp2p.io |
| 229 | +[IndexedDB]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API |
| 230 | +[S3]: https://aws.amazon.com/s3/ |
0 commit comments