@leaflink/dom-testing-utils 5.3.0 → 5.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/mocks/IntersectionObserverMock.d.ts +1 -0
- package/dist/mocks/IntersectionObserverMock.js +2 -1
- package/dist/mocks/IntersectionObserverMock.js.map +1 -1
- package/dist/utils/mockEndpoints.d.ts +1 -2
- package/dist/utils/mockEndpoints.js.map +1 -1
- package/package.json +17 -18
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
- [@leaflink/dom-testing-utils](#leaflinkdom-testing-utils)
|
|
15
15
|
- [Installation](#installation)
|
|
16
|
+
- [Releases](#releases)
|
|
16
17
|
- [Usage](#usage)
|
|
17
18
|
- [Setup file](#setup-file)
|
|
18
19
|
- [Global setup](#global-setup)
|
|
@@ -38,6 +39,67 @@
|
|
|
38
39
|
pnpm add -D @leaflink/dom-testing-utils
|
|
39
40
|
```
|
|
40
41
|
|
|
42
|
+
## Releases
|
|
43
|
+
|
|
44
|
+
Releases are fully automated using [semantic-release](https://github.com/semantic-release/semantic-release). When changes are pushed to a release branch (`main`, `alpha`, `beta`, `canary`, `next`, `next-major`, or `[0-9]+.x`), the CI workflow:
|
|
45
|
+
|
|
46
|
+
1. Builds the package with `pnpm build`
|
|
47
|
+
2. Analyzes commits to determine the next version (using [Conventional Commits](https://www.conventionalcommits.org/))
|
|
48
|
+
3. Publishes to npm and creates a GitHub release with changelog
|
|
49
|
+
|
|
50
|
+
**Commit types that trigger releases:** `feat`, `fix`, `perf`, and `revert`. Other types (e.g. `docs`, `chore`) appear in changelogs but do not bump the version on their own.
|
|
51
|
+
|
|
52
|
+
For detailed commit conventions and manual release instructions, see [CONTRIBUTING.md](.github/CONTRIBUTING.md#semantic-release).
|
|
53
|
+
|
|
54
|
+
## Dependency updates
|
|
55
|
+
|
|
56
|
+
Dependency updates are handled by self-hosted [Renovate](https://docs.renovatebot.com/), invoked from `.github/workflows/renovate.yml` using a PAT (`RENOVATE_PAT`) issued from the `leaflink-automation` account. Configuration lives in [`.github/renovate.json5`](.github/renovate.json5). Tracking ticket: [MKPL-1055](https://leaflink.atlassian.net/browse/MKPL-1055).
|
|
57
|
+
|
|
58
|
+
### Schedule
|
|
59
|
+
|
|
60
|
+
- Workflow cron wakes Renovate hourly.
|
|
61
|
+
- Renovate only opens PRs between **1am–5am America/New_York**, every day.
|
|
62
|
+
- A new release must be **≥ 10 days old** (`minimumReleaseAge`) before Renovate will propose it — buffer against compromised/withdrawn publishes.
|
|
63
|
+
- Merging is handled by a separate workflow (see [Auto-merge](#auto-merge)) that runs whenever required CI completes — independent of Renovate's schedule.
|
|
64
|
+
|
|
65
|
+
### Update groups and auto-merge policy
|
|
66
|
+
|
|
67
|
+
| Source | Update type | PR shape | Auto-merge |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| `devDependencies` | any | grouped as one PR (`chore(deps-dev): update dev dependencies`) | ✅ |
|
|
70
|
+
| `dependencies` / `peerDependencies` | **patch** | individual PR (`chore(deps): ...`) | ✅ |
|
|
71
|
+
| `dependencies` / `peerDependencies` | **minor / major** | individual PR (`chore(deps): ...`) | ❌ awaits review |
|
|
72
|
+
| `github-actions` | patch / minor / digest / pin | individual PR | ✅ |
|
|
73
|
+
| `github-actions` | major | individual PR | ❌ awaits review |
|
|
74
|
+
| Lockfile maintenance (transitive refresh of `pnpm-lock.yaml`) | weekly | `Refresh ... (chore(deps-dev))` | ✅ |
|
|
75
|
+
|
|
76
|
+
Renovate PR bodies all link back to MKPL-1055 via `prBodyNotes`.
|
|
77
|
+
|
|
78
|
+
### Auto-merge
|
|
79
|
+
|
|
80
|
+
`main` requires `quality-checks` + `release` status checks and a CODEOWNERS review. The `leaflink-automation` account (whose PAT Renovate uses) is in `main`'s `bypass_pull_request_allowances`, but Renovate itself won't call the merge API while the PR shows as `BLOCKED` in GitHub's API — even though the bypass would let it through.
|
|
81
|
+
|
|
82
|
+
To work around this, `.github/workflows/renovate-merge.yml` performs the merge on Renovate's behalf:
|
|
83
|
+
|
|
84
|
+
1. Renovate creates the PR. Any rule with `automerge: true` adds `addLabels: ['automerge']`, so the label is the single source of truth for "Renovate intended to auto-merge this."
|
|
85
|
+
2. `PR Quality Checks` and `Release` run on the PR.
|
|
86
|
+
3. When either completes, the `Renovate Merge` workflow is triggered via `workflow_run`. It:
|
|
87
|
+
- filters to PRs authored by `leaflink-automation` with the `automerge` label,
|
|
88
|
+
- asserts both required checks are present and successful,
|
|
89
|
+
- calls the merge API authenticated as `leaflink-automation`.
|
|
90
|
+
4. Because the user is on the bypass list, the merge call succeeds despite `BLOCKED` status.
|
|
91
|
+
|
|
92
|
+
Human PRs are unaffected — they still require CODEOWNERS review.
|
|
93
|
+
|
|
94
|
+
The required-checks list in `renovate-merge.yml` (`REQUIRED_CHECKS`) is hard-coded and must stay in sync with branch protection on `main`.
|
|
95
|
+
|
|
96
|
+
### Where to look
|
|
97
|
+
|
|
98
|
+
- **Dependency Dashboard issue** (auto-created by Renovate, label `dependencies`) — single source of truth for what Renovate sees, what it skipped, and why.
|
|
99
|
+
- **Workflow runs** for `Renovate` in the Actions tab — `workflow_dispatch` with `logLevel: debug` to debug Renovate itself.
|
|
100
|
+
- **Workflow runs** for `Renovate Merge` in the Actions tab — shows which PRs were merged or skipped (with reason) when CI completed. `workflow_dispatch` for manual recovery.
|
|
101
|
+
- **`config:recommended`** is the base preset; any unspecified behavior comes from there.
|
|
102
|
+
|
|
41
103
|
## Usage
|
|
42
104
|
|
|
43
105
|
In your test files you can import utility functions.
|
|
@@ -6,6 +6,7 @@ export interface IntersectionObserverMockInterface extends IntersectionObserver
|
|
|
6
6
|
export declare class IntersectionObserverMock implements IntersectionObserverMockInterface {
|
|
7
7
|
root: IntersectionObserver['root'];
|
|
8
8
|
rootMargin: IntersectionObserver['rootMargin'];
|
|
9
|
+
scrollMargin: IntersectionObserver['scrollMargin'];
|
|
9
10
|
thresholds: IntersectionObserver['thresholds'];
|
|
10
11
|
target: Element | null;
|
|
11
12
|
disconnect: IntersectionObserver['disconnect'];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class IntersectionObserverMock {
|
|
2
2
|
root = null;
|
|
3
3
|
rootMargin = '0px 0px 0px 0px';
|
|
4
|
+
scrollMargin = '0px 0px 0px 0px';
|
|
4
5
|
thresholds = [];
|
|
5
6
|
target = null;
|
|
6
7
|
disconnect = IntersectionObserverMock.prototype.disconnect.bind(this);
|
|
@@ -35,7 +36,7 @@ export function mockIntersectionObserver() {
|
|
|
35
36
|
});
|
|
36
37
|
IntersectionObserverMock.prototype.unobserve = vi.fn(function () {
|
|
37
38
|
this.target = null;
|
|
38
|
-
window.removeEventListener('scroll', this.onScroll);
|
|
39
|
+
window.removeEventListener('scroll', this.onScroll.bind(this));
|
|
39
40
|
});
|
|
40
41
|
vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
|
|
41
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntersectionObserverMock.js","sourceRoot":"src/","sources":["mocks/IntersectionObserverMock.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,wBAAwB;IACnC,IAAI,GAAiC,IAAI,CAAC;IAC1C,UAAU,GAAuC,iBAAiB,CAAC;IACnE,UAAU,GAAuC,EAAE,CAAC;IACpD,MAAM,GAAmB,IAAI,CAAC;IAE9B,UAAU,GAAuC,wBAAwB,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1G,OAAO,GAAoC,wBAAwB,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEjG,WAAW,GAAwC,wBAAwB,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE7G,SAAS,GAAsC,wBAAwB,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEvG,cAAc,CAA+B;IAE7C,QAAQ,CAAC,CAAQ;QACf,IAAI,CAAC,CAAC,CAAC,YAAY,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAErB,IAAI,CAAC,cAAc,CACjB;YACE;gBACE,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;gBAC3C,cAAc,EAAE,MAAM,CAAC,cAAc;aACT;SAC/B,EACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,cAAc,CAAC,CAClD,CAAC;IACJ,CAAC;IAED,YAAY,QAAsC;QAChD,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC;QAE/B,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,CAAC;CACF;AAED,MAAM,UAAU,wBAAwB;IACtC,wBAAwB,CAAC,SAAS,CAAC,UAAU,GAAG,EAAE,CAAC,EAAE,CAAC,cAA2C,CAAC,CAAC,CAAC;IAEpG,wBAAwB,CAAC,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,UAA0C,OAAgB;QAC3G,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,wBAAwB,CAAC,SAAS,CAAC,WAAW,GAAG,EAAE,CAAC,EAAE,CAAC;QAGrD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,wBAAwB,CAAC,SAAS,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"IntersectionObserverMock.js","sourceRoot":"src/","sources":["mocks/IntersectionObserverMock.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { type DefaultBodyType, http, type PathParams, type RequestHandlerOptions, type ResponseResolver } from 'msw';
|
|
2
|
-
import { type HttpRequestResolverExtras } from 'msw/lib/core/handlers/HttpHandler';
|
|
1
|
+
import { type DefaultBodyType, http, type HttpRequestResolverExtras, type PathParams, type RequestHandlerOptions, type ResponseResolver } from 'msw';
|
|
3
2
|
import { SetupServer } from 'msw/node';
|
|
4
3
|
export type RestMethod = keyof typeof http;
|
|
5
4
|
export default function createMockApiUtils(server: SetupServer): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mockEndpoints.js","sourceRoot":"src/","sources":["utils/mockEndpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,
|
|
1
|
+
{"version":3,"file":"mockEndpoints.js","sourceRoot":"src/","sources":["utils/mockEndpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,EAEJ,YAAY,GAIb,MAAM,KAAK,CAAC;AAKb,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,MAAmB;IAC5D;;OAEG;IACH,MAAM,gBAAgB,GAAG,CACvB,GAAW,EACX,YAA0B,EAC1B,MAAkB,EAClB,OAA+B,EAC/B,EAAE;QACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAChF,CAAC,CAAC;IAEF;;OAEG;IACH,MAAM,YAAY,GAAG,CAKnB,WAAmB,EACnB,QAAgG,EAChG,MAAkB,EAClB,OAA+B,EAC/B,EAAE;QACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAC;IAEF;;;OAGG;IACH,MAAM,gBAAgB,GAAG,CAAC,MAAkB,EAAE,EAAE,CAAC,CAAC,GAAW,EAAE,YAAY,EAAE,OAA+B,EAAE,EAAE;QAC9G,OAAO,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC;IAEF;;;OAGG;IACH,MAAM,oBAAoB,GACxB,CAKE,MAAkB,EAClB,EAAE,CACJ,CACE,GAAW,EACX,QAAgG,EAChG,OAA+B,EAC/B,EAAE;QACF,OAAO,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC,CAAC;IAEJ,OAAO;QACL,gBAAgB;QAChB,YAAY;QACZ,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC;QACpC,YAAY,EAAE,gBAAgB,CAAC,MAAM,CAAC;QACtC,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC;QACpC,cAAc,EAAE,gBAAgB,CAAC,QAAQ,CAAC;QAC1C,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC;QACxC,eAAe,EAAE,oBAAoB,CAAC,KAAK,CAAC;QAC5C,gBAAgB,EAAE,oBAAoB,CAAC,MAAM,CAAC;QAC9C,eAAe,EAAE,oBAAoB,CAAC,KAAK,CAAC;QAC5C,kBAAkB,EAAE,oBAAoB,CAAC,QAAQ,CAAC;QAClD,iBAAiB,EAAE,oBAAoB,CAAC,OAAO,CAAC;KACjD,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leaflink/dom-testing-utils",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.2",
|
|
4
4
|
"description": "Frontend DOM testing utilities",
|
|
5
|
-
"packageManager": "pnpm@
|
|
5
|
+
"packageManager": "pnpm@11.1.2",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24",
|
|
8
|
-
"pnpm": ">=
|
|
8
|
+
"pnpm": ">=11"
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
@@ -41,31 +41,30 @@
|
|
|
41
41
|
},
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"msw": "^2.
|
|
44
|
+
"msw": "^2.14.6"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@leaflink/eslint-config": "^
|
|
48
|
-
"@
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
47
|
+
"@leaflink/eslint-config": "^5.3.0",
|
|
48
|
+
"@types/node": "^25.9.0",
|
|
49
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
50
|
+
"commitlint": "^21.0.0",
|
|
51
|
+
"conventional-changelog-conventionalcommits": "^9.3.0",
|
|
52
|
+
"eslint": "^10.0.0",
|
|
53
|
+
"jsdom": "^29.1.1",
|
|
54
|
+
"semantic-release": "^25.0.3",
|
|
55
|
+
"stylelint": "^17.11.0",
|
|
56
|
+
"typescript": "^6.0.0",
|
|
57
|
+
"vite": "^8.0.13",
|
|
58
|
+
"vitest": "^4.1.6"
|
|
55
59
|
},
|
|
56
60
|
"peerDependencies": {
|
|
57
61
|
"@testing-library/dom": "^7 || ^8 || ^9 || ^10",
|
|
58
|
-
"@testing-library/jest-dom": "^
|
|
62
|
+
"@testing-library/jest-dom": "^6",
|
|
59
63
|
"@testing-library/user-event": "^12 || ^13 || ^14",
|
|
60
64
|
"@testing-library/vue": "^7 || ^8",
|
|
61
65
|
"@vue/test-utils": "^2",
|
|
62
66
|
"vue": "^3.2.31"
|
|
63
67
|
},
|
|
64
|
-
"eslintConfig": {
|
|
65
|
-
"extends": [
|
|
66
|
-
"leaflink"
|
|
67
|
-
]
|
|
68
|
-
},
|
|
69
68
|
"commitlint": {
|
|
70
69
|
"extends": [
|
|
71
70
|
"@leaflink/eslint-config/commitlint"
|