diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74dd8e1..d03d304 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - main pull_request: {} jobs: diff --git a/.github/workflows/plan-release.yml b/.github/workflows/plan-release.yml index 7b78cf7..e8126d6 100644 --- a/.github/workflows/plan-release.yml +++ b/.github/workflows/plan-release.yml @@ -3,7 +3,6 @@ on: push: branches: - main - - master pull_request_target: # This workflow has permissions on the repo, do NOT run code from PRs in this workflow. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ types: - labeled @@ -24,7 +23,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - ref: 'master' + ref: 'main' # This will only cause the `check-plan` job to have a "command" of `release` # when the .release-plan.json file was changed on the last commit. - id: check-release @@ -51,7 +50,7 @@ jobs: # github-changelog can discover what's changed since the last release with: fetch-depth: 0 - ref: 'master' + ref: 'main' - uses: actions/setup-node@v4 with: node_version: 20 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index db44b3b..f8f5831 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,4 @@ -# For every push to the master branch, this checks if the release-plan was +# For every push to the main branch, this checks if the release-plan was # updated and if it was it will publish stable npm packages based on the # release plan @@ -8,7 +8,7 @@ on: workflow_dispatch: push: branches: - - master + - main concurrency: group: publish-${{ github.head_ref || github.ref }} @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - ref: 'master' + ref: 'main' # This will only cause the `check-plan` job to have a result of `success` # when the .release-plan.json file was changed on the last commit. This # plus the fact that this action only runs on main will be enough of a guard diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa94477 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# memory-leak-detector + +Memory Leak Detector is a tiny node server that exposes an HTTP API for making snapshots and queries against a Google Chrome instance, typically from tests. + +It requires a path to the source code so it can scan codebase for `class`. + +`pnpm memory-leak-detector ./app` + +## Example app + +An example app can be found [here](/tree/main/packages/test-app). + +### Running it + +- checkout the repo +- `pnpm install` +- `pnpm test:memory-leak-ember` + +## Usage + +Install the `memory-leak-detector` dependency + +- `pnpm add -D memory-leak-detector` + +Define a script that runs a `memory-leak-detector` node server and listen, run your tests as usual, finally wait for the process results. + +```sh +pnpm memory-leak-detector ./app & pid=$!; pnpm test; wait $pid +``` + +The reason for waiting for that process' pid is that e.g. Qunit doesn't have a good way to dynamically create and run a test at the end of your test suite while we must have a way to fail the CI in case something's wrong. +For that reason `detectLeakingClasses` is meant to be used at the end only, because it always exits the process with either fail or success. + +That also means that if you don't intend to use `detectLeakingClasses`, your script should not wait for the `memory-leak-detector` to exit i.e. + +```sh +pnpm memory-leak-detector ./app & pnpm test +``` + +### Checking for memory leaks after test suite finishes + +The following code checks for any known, dangling objects (Classes) that `memory-leak-detector` had found in the codebase. + +```js +// tests/test-helper.js + +// this checks whether there are any of `our` classes retained after all tests have passed. +globalThis.Testem?.afterTests(async (_config, _data, callback) => { + // Give some time for Garbage collector to kick in + await new Promise((resolve) => setTimeout(resolve, 5000)); + + await detectLeakingClasses("title", document.title); + callback(); +}); +``` + +This approach doesn't catch all memory-leaks as it will only detect the ones that have manipulated a window/document objects by e.g. storing references or not removed `addEventListener` calls. + +### Asserting object count + +The following code performs an assertion against current memory state of the browser while the test and an application is running. + +```js +test("paginating back and forth", async function (assert) { + // Initial load and each page has 30 items + await visit("users"); + + await click("[data-test-pagination-next]"); + await click("[data-test-pagination-next]"); + await click("[data-test-pagination-previous]"); + + const assertions = { + UserListItemComponent: 30, + }; + + const results = await detectMemoryLeak( + "url", + document.location.href, + assertions, + ); // { UserListItemComponent: 120 } + + assert.deepEqual(results, assertions); + assert.strictEqual(currentURL(), "users"); +}); +```