├── .eslintrc ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .qualscanrc ├── CHANGELOG.md ├── LICENSE ├── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── readme.md ├── src ├── heap.js ├── profiler.js └── utils.js ├── tests ├── fsFailed.test.js ├── heap.test.js └── profiler.test.js └── tsconfig.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "env": { 4 | "jest": true 5 | }, 6 | "rules": { 7 | "indent": ["error", 4] 8 | } 9 | } -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | 3 | name: Node.js CI 4 | 5 | on: 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | node-version: [16.x, 18.x, 20.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm ci 27 | - run: npm run linter 28 | - run: npm run types 29 | 30 | - name: Run the qualscan tool 31 | run: | 32 | npm install qualscan -g 33 | qualscan --scripts 34 | 35 | - name: Run the tests and generate coverage report 36 | run: npm run test 37 | 38 | - name: Upload coverage to Codecov 39 | uses: codecov/codecov-action@v3 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/dist 2 | **/*.d.ts 3 | **/open-source 4 | **/*.zip 5 | **/node_modules 6 | **/.DS_Store 7 | **/test_files 8 | .idea 9 | **/coverage 10 | -------------------------------------------------------------------------------- /.qualscanrc: -------------------------------------------------------------------------------- 1 | { 2 | "require-time": { 3 | "budget": { 4 | "fail": { "entrypointTime": 1500000000 } 5 | } 6 | }, 7 | "dependencies-size": { 8 | "budget": { 9 | "fail": { "weight": 200000000, "dependencies": 95, "directDependencies": 5, "depth": 6 } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 1.4.6 released on 2022-12-22 2 | 3 | ## Features 4 | 5 | **Upgrade dependencies** 6 | 7 | Upgrades dependencies version. 8 | 9 | [440bf4a](https://github.com/wallet77/v8-inspector-api/releases/tag/1.4.6) - Version 1.4.6 10 | 11 | # Version 1.4.1 released on 2021-06-10 12 | 13 | ## Features 14 | 15 | **Add TS config and types** 16 | 17 | This feature will facilitate the work of the devs when they use this module. 18 | 19 | [6f932d3](https://github.com/wallet77/v8-inspector-api/releases/tag/1.4.1) - Version 1.4.1 20 | 21 | # Version 1.4.0 released on 2021-06-01 22 | 23 | ## Features 24 | 25 | **Switch to the new AWS SDK** 26 | 27 | This feature will reduce package size and improve performances. 28 | 29 | [9be9b5d](https://github.com/wallet77/v8-inspector-api/releases/tag/1.4.0) - Version 1.4.0 30 | 31 | # Version 1.x released on 2019-10-03 32 | 33 | ## Features 34 | 35 | **CPU profiling** 36 | 37 | This feature will trigger a CPU profiling and save data (file, export to S3, return raw data) 38 | 39 | [7f7265f](https://github.com/wallet77/v8-inspector-api/commit/7f7265f50d31498ddf3a88534a3894ebfee0606f) - Version 1.0.0 40 | 41 | **Memory Snapshot** 42 | 43 | This feature can trigger memory snapshot (or memory sampling). 44 | 45 | [7f7265f](https://github.com/wallet77/v8-inspector-api/commit/7f7265f50d31498ddf3a88534a3894ebfee0606f) - Version 1.0.0 46 | 47 | **Code coverage** 48 | 49 | This feature retrieve code coverage data during runtime. 50 | 51 | [7ab4df1](https://github.com/wallet77/v8-inspector-api/commit/7ab4df1bb0094cdc8fcad6e7563612d6a87e1125) - Add coverage feature. 52 | 53 | **Export to S3** 54 | 55 | This feature allow you to export every generated data to S3. 56 | 57 | [7f7265f](https://github.com/wallet77/v8-inspector-api/commit/7f7265f50d31498ddf3a88534a3894ebfee0606f) - Version 1.0.0 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vincent Vallet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const inspector = require('inspector') 4 | const Profiler = require('./src/profiler') 5 | const Heap = require('./src/heap') 6 | 7 | class Inspector { 8 | constructor (config = {}) { 9 | if (!config.aws) config.aws = { region: 'eu-west-1' } 10 | 11 | let client = null 12 | if (!config.storage) config.storage = { type: 'raw' } 13 | if (config.storage.type === 's3') { 14 | const { S3Client } = require('@aws-sdk/client-s3') 15 | client = new S3Client(config.aws) 16 | } 17 | 18 | const session = new inspector.Session() 19 | session.connect() 20 | 21 | this.session = session 22 | 23 | this.profiler = new Profiler(this.session, config, client) 24 | this.heap = new Heap(this.session, config, client) 25 | } 26 | 27 | getCurrentSession () { 28 | return this.session 29 | } 30 | 31 | async destroy () { 32 | await this.profiler.disable() 33 | await this.heap.disable() 34 | this.session.disconnect() 35 | this.session = null 36 | } 37 | } 38 | 39 | module.exports = Inspector 40 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | collectCoverage: true, 4 | collectCoverageFrom: ['src/**/*.js', 'index.js'], 5 | coverageDirectory: './coverage', 6 | coverageThreshold: { 7 | global: { 8 | branches: 80, 9 | functions: 80, 10 | lines: 80, 11 | statements: 80 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inspector-api", 3 | "version": "1.4.11", 4 | "main": "index.js", 5 | "bin": "./index.js", 6 | "types": "./index.d.ts", 7 | "exports": { 8 | "require": "./index.js", 9 | "import": "./index.js" 10 | }, 11 | "files": [ 12 | "index.js", 13 | "index.d.ts", 14 | "src/**" 15 | ], 16 | "scripts": { 17 | "types": "tsc -p tsconfig.json", 18 | "test": "NODE_ENV=test ./node_modules/.bin/jest", 19 | "linter": "./node_modules/.bin/eslint ./" 20 | }, 21 | "keywords": [ 22 | "v8", 23 | "inspector", 24 | "profiler", 25 | "profiling", 26 | "cpu", 27 | "memory", 28 | "coverage", 29 | "heap", 30 | "HeapSnapshot", 31 | "performance", 32 | "sampling" 33 | ], 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/wallet77/v8-inspector-api.git" 37 | }, 38 | "author": "wallet77", 39 | "license": "MIT", 40 | "description": "A simple node module to access V8 inspector + some tools to export and read the data.", 41 | "engines": { 42 | "node": ">=14.0.0", 43 | "npm": ">=6.0.0" 44 | }, 45 | "engineStrict": true, 46 | "devDependencies": { 47 | "eslint": "^8.57.0", 48 | "eslint-config-standard": "^17.1.0", 49 | "eslint-plugin-import": "^2.31.0", 50 | "eslint-plugin-n": "^16.6.2", 51 | "eslint-plugin-promise": "^6.1.1", 52 | "jest": "29.7.0", 53 | "typescript": "^5.7.3" 54 | }, 55 | "dependencies": { 56 | "@aws-sdk/client-s3": "^3.726.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![GitHub release](https://img.shields.io/npm/v/inspector-api.svg)](https://github.com/wallet77/v8-inspector-api/releases/) 2 | [![GitHub license](https://img.shields.io/github/license/wallet77/v8-inspector-api.svg)](https://github.com/wallet77/v8-inspector-api/blob/master/LICENSE) 3 | 4 | [![CI pipeline](https://github.com/wallet77/v8-inspector-api/workflows/Node.js%20CI/badge.svg)](https://github.com/wallet77/v8-inspector-api/actions?query=workflow%3A%22Node.js+CI%22) 5 | [![Code coverage](https://codecov.io/gh/wallet77/v8-inspector-api/branch/master/graph/badge.svg)](https://codecov.io/gh/wallet77/v8-inspector-api) 6 | [![Opened issues](https://img.shields.io/github/issues-raw/wallet77/v8-inspector-api)](https://github.com/wallet77/v8-inspector-api/issues) 7 | [![Opened PR](https://img.shields.io/github/issues-pr-raw/wallet77/v8-inspector-api)](https://github.com/wallet77/v8-inspector-api/pulls) 8 | [![DeepScan grade](https://deepscan.io/api/teams/12061/projects/15020/branches/292505/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=12061&pid=15020&bid=292505) 9 | [![Node version](https://img.shields.io/node/v-lts/inspector-api.svg)](https://github.com/wallet77/v8-inspector-api) 10 | 11 | # Purpose 12 | 13 | Simple wrapper around "inspector" module. 14 | Basically it adds : 15 | - promises & async/await syntax 16 | - S3 exporter 17 | 18 | # Compatibility 19 | 20 | | Version | Supported | Tested | 21 | |:-------------:|:-------------:|:--------------:| 22 | | 20.x | yes | yes | 23 | | 18.x | yes | yes | 24 | | 16.x | yes | yes | 25 | 26 | **In order to have all features we recommend to use at least Node.js version 10 or higher.** 27 | 28 | # Installation 29 | 30 | ```console 31 | $ npm install inspector-api --save 32 | ``` 33 | 34 | # Usage 35 | 36 | ## CPU profiling 37 | ```javascript 38 | const Inspector = require('inspector-api') 39 | const inspector = new Inspector() 40 | 41 | await inspector.profiler.enable() 42 | await inspector.profiler.start() 43 | // Invoke business logic under measurement here... 44 | 45 | // some time later... 46 | await inspector.profiler.stop() 47 | 48 | ``` 49 | 50 | ## Memory sampling 51 | ```javascript 52 | const Inspector = require('inspector-api') 53 | const inspector = new Inspector() 54 | 55 | await inspector.heap.enable() 56 | await inspector.heap.startSampling() 57 | // Invoke business logic under measurement here... 58 | 59 | // some time later... 60 | await inspector.heap.stopSampling() 61 | 62 | ``` 63 | 64 | ## Memory snapshot 65 | ```javascript 66 | const Inspector = require('inspector-api') 67 | const inspector = new Inspector() 68 | 69 | await inspector.heap.takeSnapshot() 70 | 71 | ``` 72 | 73 | ## Code coverage 74 | ```javascript 75 | const Inspector = require('inspector-api') 76 | const inspector = new Inspector() 77 | 78 | await inspector.profiler.enable() 79 | await inspector.profiler.startPreciseCoverage({ callCount: true, detailed: true }) 80 | 81 | const data = await inspector.profiler.takePreciseCoverage() 82 | await inspector.profiler.stopPreciseCoverage() 83 | 84 | ``` 85 | 86 | ## Use S3 exporter 87 | ```javascript 88 | const Inspector = require('inspector-api') 89 | const inspector = new Inspector({ 90 | storage: { 91 | type: 's3', 92 | bucket: 'testBucket', 93 | dir: 'inspector' 94 | } 95 | }) 96 | 97 | await inspector.profiler.enable() 98 | await inspector.profiler.start() 99 | // Invoke business logic under measurement here... 100 | 101 | // some time later... 102 | await inspector.profiler.stop() 103 | 104 | ``` 105 | 106 | **Warning: it seems that the new AWS SDK leads to unexpected error if you use the takeSnapshot method (you should use memory sampling)** 107 | 108 | ### Constructor's config 109 | 110 | ```javascript 111 | new inspector([config]) 112 | ``` 113 | 114 | #### config.storage 115 | 116 | | Option | description | Default value | 117 | |:-------------:|:------------------------------------------:|:--------------:| 118 | | `type` | Storage type (raw, s3 or fs) | raw | 119 | | `bucket` | S3 bucket's name | none | 120 | | `dir` | Directory where to store the file | none | 121 | 122 | If you use fs, the generated data will be store on the disk in your default tmp directory. 123 | You can display it in Node.js with the command `require('os').tmpdir()` 124 | 125 | # Test 126 | 127 | ```console 128 | $ npm test 129 | ``` 130 | 131 | Coverage report can be found in coverage/. 132 | -------------------------------------------------------------------------------- /src/heap.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const utils = require('./utils') 4 | 5 | class Heap { 6 | constructor (session, config, s3Client) { 7 | this.s3Client = s3Client 8 | this.session = session 9 | this.config = config 10 | } 11 | 12 | async enable () { 13 | await utils.invokeFunction(this.session, 'HeapProfiler.enable') 14 | } 15 | 16 | async disable () { 17 | await utils.invokeFunction(this.session, 'HeapProfiler.disable') 18 | } 19 | 20 | async startSampling () { 21 | await utils.invokeFunction(this.session, 'HeapProfiler.startSampling') 22 | } 23 | 24 | async stopSampling () { 25 | return utils.invokeStop('HeapProfiler.stopSampling', this.session, 'heapprofiler', 'heapprofile', this.config, this.s3Client) 26 | } 27 | 28 | takeSnapshot () { 29 | return new Promise((resolve, reject) => { 30 | const res = [] 31 | const getChunk = (m) => { 32 | res.push(m.params.chunk) 33 | } 34 | 35 | this.session.on('HeapProfiler.addHeapSnapshotChunk', getChunk) 36 | 37 | this.session.post('HeapProfiler.takeHeapSnapshot', null, (err, r) => { 38 | this.session.removeListener('HeapProfiler.addHeapSnapshotChunk', getChunk) 39 | 40 | if (err) return reject(err) 41 | 42 | const date = new Date() 43 | const fileName = `profile_${date.getTime()}.heapsnapshot` 44 | 45 | utils.writeData(JSON.parse(res.join('')), fileName, this.config, this.s3Client).then((data) => { 46 | resolve(data) 47 | }).catch(err => reject(err)) 48 | }) 49 | }) 50 | } 51 | } 52 | 53 | module.exports = Heap 54 | -------------------------------------------------------------------------------- /src/profiler.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const utils = require('./utils') 4 | 5 | class Profiler { 6 | constructor (session, config, s3Client) { 7 | this.s3Client = s3Client 8 | this.session = session 9 | this.config = config 10 | } 11 | 12 | async enable () { 13 | await utils.invokeFunction(this.session, 'Profiler.enable') 14 | } 15 | 16 | async disable () { 17 | await utils.invokeFunction(this.session, 'Profiler.disable') 18 | } 19 | 20 | async start () { 21 | await utils.invokeFunction(this.session, 'Profiler.start') 22 | } 23 | 24 | async stop () { 25 | return utils.invokeStop('Profiler.stop', this.session, 'profile', 'cpuprofile', this.config, this.s3Client) 26 | } 27 | 28 | async startPreciseCoverage (args) { 29 | return utils.invokeFunction(this.session, 'Profiler.startPreciseCoverage', args) 30 | } 31 | 32 | async stopPreciseCoverage () { 33 | return utils.invokeFunction(this.session, 'Profiler.stopPreciseCoverage') 34 | } 35 | 36 | async takePreciseCoverage () { 37 | return utils.invokeStop('Profiler.takePreciseCoverage', this.session, 'coverage', 'json', this.config, this.s3Client) 38 | } 39 | } 40 | 41 | module.exports = Profiler 42 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs').promises 2 | const os = require('os') 3 | 4 | const writeData = async (data, fileName, config, s3Client) => { 5 | if (config.storage.type === 'fs') { 6 | const tmpDir = os.tmpdir() 7 | await fs.writeFile(`${tmpDir}/${fileName}`, JSON.stringify(data)) 8 | } else if (config.storage.type === 's3') { 9 | const { PutObjectCommand } = require('@aws-sdk/client-s3') 10 | const params = { 11 | Body: JSON.stringify(data), 12 | Bucket: config.storage.bucket, 13 | Key: `${config.storage.dir}/${fileName}`, 14 | ContentType: 'application/json' 15 | } 16 | const command = new PutObjectCommand(params) 17 | await s3Client.send(command) 18 | } 19 | return data 20 | } 21 | 22 | module.exports = { 23 | writeData: writeData, 24 | 25 | invokeFunction: (session, fnName, args = {}) => { 26 | return new Promise((resolve, reject) => { 27 | session.post(fnName, args, (err) => { 28 | if (err) return reject(err) 29 | resolve() 30 | }) 31 | }) 32 | }, 33 | 34 | invokeStop: (fnName, session, suffix, ext, config, s3Client) => { 35 | return new Promise((resolve, reject) => { 36 | session.post(fnName, (err, res) => { 37 | if (err) return reject(err) 38 | 39 | const data = res.profile || res.result 40 | 41 | const date = new Date() 42 | const fileName = `${suffix}_${date.getTime()}.${ext}` 43 | 44 | writeData(data, fileName, config, s3Client).then((data) => { 45 | resolve(data) 46 | }).catch(err => reject(err)) 47 | }) 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/fsFailed.test.js: -------------------------------------------------------------------------------- 1 | const Inspector = require('../index') 2 | 3 | jest.mock('fs', () => ({ 4 | promises: { 5 | writeFile: (filename, data) => { 6 | throw new Error('writing failed!') 7 | } 8 | } 9 | })) 10 | 11 | describe('Make fs failed', () => { 12 | let inspector = null 13 | 14 | it('collect data and write it on the disk but failed', async () => { 15 | inspector = new Inspector({ 16 | storage: { 17 | type: 'fs' 18 | } 19 | }) 20 | 21 | try { 22 | await inspector.profiler.enable() 23 | await inspector.profiler.start() 24 | await inspector.profiler.stop() 25 | throw new Error('It should have failed!') 26 | } catch (err) { 27 | expect(err.message).toEqual('writing failed!') 28 | } 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/heap.test.js: -------------------------------------------------------------------------------- 1 | const Inspector = require('../index') 2 | const utils = require('../src/utils') 3 | 4 | describe('Heap', () => { 5 | describe('Take snapshot', () => { 6 | let inspector = null 7 | 8 | afterEach(() => { 9 | inspector.destroy() 10 | }) 11 | 12 | jest.setTimeout(10000) 13 | 14 | it('collect raw data', async () => { 15 | inspector = new Inspector({ 16 | storage: { type: 'fs' } 17 | }) 18 | 19 | const data = await inspector.heap.takeSnapshot() 20 | 21 | expect(Object.prototype.hasOwnProperty.call(data, 'snapshot')).toEqual(true) 22 | }) 23 | 24 | it('should failed on takeHeapSnapshot', async () => { 25 | inspector = new Inspector({ 26 | storage: { type: 'raw' } 27 | }) 28 | const oldImpl = inspector.profiler.session.post 29 | 30 | inspector.heap.session.post = (name, opts, cb) => { if (cb) cb(new Error('takeHeapSnapshot failed')) } 31 | 32 | try { 33 | await inspector.heap.takeSnapshot() 34 | throw new Error('Should have failed!') 35 | } catch (err) { 36 | inspector.profiler.session.post = oldImpl 37 | expect(err.message).toEqual('takeHeapSnapshot failed') 38 | } 39 | }) 40 | 41 | it('should failed on takeHeapSnapshot because of writeData', async () => { 42 | inspector = new Inspector({ 43 | storage: { type: 'raw' } 44 | }) 45 | 46 | const spy = jest.spyOn(utils, 'writeData').mockReturnValue(Promise.reject(new Error('writeData failed'))) 47 | 48 | try { 49 | await inspector.heap.takeSnapshot() 50 | throw new Error('Should have failed!') 51 | } catch (err) { 52 | expect(err.message).toEqual('writeData failed') 53 | expect(spy).toHaveBeenCalledTimes(1) 54 | } 55 | }) 56 | 57 | it('collect sampling raw data', async () => { 58 | inspector = new Inspector() 59 | 60 | await inspector.heap.enable() 61 | 62 | await inspector.heap.startSampling() 63 | 64 | const data = await inspector.heap.stopSampling() 65 | expect(Object.keys(data).length > 0).toEqual(true) 66 | }) 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /tests/profiler.test.js: -------------------------------------------------------------------------------- 1 | require('@aws-sdk/client-s3') 2 | 3 | jest.mock('@aws-sdk/client-s3', () => { 4 | return { 5 | S3Client: function PutObjectCommand () { 6 | this.send = (command) => { 7 | if (command.params.Bucket === 'fail') return Promise.reject(new Error()) 8 | return Promise.resolve() 9 | } 10 | }, 11 | PutObjectCommand: function PutObjectCommand (params) { 12 | this.params = params 13 | } 14 | } 15 | }) 16 | 17 | const Inspector = require('../index') 18 | 19 | const startAndStopAndCheckData = async (inspector) => { 20 | await inspector.profiler.enable() 21 | await inspector.profiler.start() 22 | 23 | const data = await inspector.profiler.stop() 24 | expect(Object.keys(data)).toEqual(['nodes', 'startTime', 'endTime', 'samples', 'timeDeltas']) 25 | } 26 | 27 | describe('Profiler', () => { 28 | afterEach(() => { 29 | jest.resetModules() 30 | }) 31 | 32 | describe('General methods', () => { 33 | it('get current Session', async () => { 34 | const inspector = new Inspector() 35 | await inspector.profiler.enable() 36 | const session = inspector.getCurrentSession() 37 | 38 | expect(typeof session).toEqual('object') 39 | 40 | await inspector.destroy() 41 | 42 | expect(inspector.getCurrentSession()).toEqual(null) 43 | }) 44 | }) 45 | 46 | describe('CPU profiler', () => { 47 | let inspector = null 48 | 49 | afterEach(async () => { 50 | await inspector.destroy() 51 | jest.resetAllMocks() 52 | }) 53 | 54 | it('collect raw data', async () => { 55 | inspector = new Inspector() 56 | await startAndStopAndCheckData(inspector) 57 | }) 58 | 59 | it('collect data and send to s3', async () => { 60 | inspector = new Inspector({ 61 | storage: { 62 | type: 's3', 63 | bucket: 'testBucket', 64 | dir: 'inspector' 65 | } 66 | }) 67 | 68 | await inspector.profiler.enable() 69 | await inspector.profiler.start() 70 | 71 | await inspector.profiler.stop() 72 | }) 73 | 74 | it('collect data but fail to send to s3', async () => { 75 | inspector = new Inspector({ 76 | storage: { 77 | type: 's3', 78 | bucket: 'testBucket', 79 | dir: 'inspector' 80 | } 81 | }) 82 | 83 | await inspector.profiler.enable() 84 | await inspector.profiler.start() 85 | 86 | try { 87 | await inspector.profiler.stop() 88 | } catch (err) { 89 | expect(err.message).toEqual('S3 failed') 90 | } 91 | }) 92 | 93 | it('collect data and write it on the disk', async () => { 94 | inspector = new Inspector({ 95 | storage: { 96 | type: 'fs' 97 | } 98 | }) 99 | 100 | await startAndStopAndCheckData(inspector) 101 | }) 102 | 103 | it('enable() fail', async () => { 104 | inspector = new Inspector() 105 | const oldImpl = inspector.profiler.session.post 106 | inspector.profiler.session.post = (name, args, cb) => { cb(new Error('enable failed')) } 107 | 108 | try { 109 | await inspector.profiler.enable() 110 | await inspector.profiler.start() 111 | await inspector.profiler.stop() 112 | throw new Error('Should have failed!') 113 | } catch (err) { 114 | inspector.profiler.session.post = oldImpl 115 | expect(err.message).toEqual('enable failed') 116 | } 117 | }) 118 | 119 | it('start() fail', async () => { 120 | inspector = new Inspector() 121 | const oldImpl = inspector.profiler.session.post 122 | 123 | try { 124 | await inspector.profiler.enable() 125 | inspector.profiler.session.post = (name, args, cb) => { cb(new Error('start failed')) } 126 | await inspector.profiler.start() 127 | await inspector.profiler.stop() 128 | throw new Error('Should have failed!') 129 | } catch (err) { 130 | inspector.profiler.session.post = oldImpl 131 | expect(err.message).toEqual('start failed') 132 | } 133 | }) 134 | 135 | it('stop() fail', async () => { 136 | inspector = new Inspector() 137 | const oldImpl = inspector.profiler.session.post 138 | 139 | try { 140 | await inspector.profiler.enable() 141 | await inspector.profiler.start() 142 | inspector.profiler.session.post = (name, cb) => { cb(new Error('stop failed')) } 143 | await inspector.profiler.stop() 144 | throw new Error('Should have failed!') 145 | } catch (err) { 146 | inspector.profiler.session.post = oldImpl 147 | expect(err.message).toEqual('stop failed') 148 | } 149 | }) 150 | }) 151 | 152 | describe('Coverage', () => { 153 | let inspector = null 154 | 155 | afterEach(async () => { 156 | await inspector.destroy() 157 | }) 158 | 159 | it('collect raw data', async () => { 160 | inspector = new Inspector() 161 | await inspector.profiler.enable() 162 | await inspector.profiler.startPreciseCoverage({ callCount: true, detailed: true }) 163 | 164 | const data = await inspector.profiler.takePreciseCoverage() 165 | await inspector.profiler.stopPreciseCoverage() 166 | 167 | const detectProfilerFile = data.find((item) => { 168 | return item.url.indexOf('profiler.test.js') > -1 169 | }) 170 | 171 | expect(detectProfilerFile).toBeDefined() 172 | }) 173 | }) 174 | }) 175 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "noEmit": false, 6 | "emitDeclarationOnly": true, 7 | "declaration": true 8 | }, 9 | "include": [ 10 | "index.js", 11 | "src/" 12 | ] 13 | } 14 | --------------------------------------------------------------------------------