@shopify/ui-extensions-tester 0.0.0-snapshot-20260316150650

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.
Files changed (131) hide show
  1. package/README.md +461 -0
  2. package/admin.esnext +1 -0
  3. package/admin.js +1 -0
  4. package/admin.mjs +1 -0
  5. package/build/cjs/admin/factories.js +323 -0
  6. package/build/cjs/admin/index.js +70 -0
  7. package/build/cjs/api-version.js +22 -0
  8. package/build/cjs/checkout/factories.js +417 -0
  9. package/build/cjs/checkout/index.js +162 -0
  10. package/build/cjs/customer-account/factories.js +235 -0
  11. package/build/cjs/customer-account/index.js +89 -0
  12. package/build/cjs/fetch-polyfills.js +193 -0
  13. package/build/cjs/index.js +421 -0
  14. package/build/cjs/mocks/i18n.js +21 -0
  15. package/build/cjs/mocks/money.js +16 -0
  16. package/build/cjs/mocks/signals.js +34 -0
  17. package/build/cjs/mocks/target-apis.js +37 -0
  18. package/build/cjs/navigation.js +56 -0
  19. package/build/cjs/point-of-sale/factories.js +510 -0
  20. package/build/cjs/point-of-sale/index.js +85 -0
  21. package/build/cjs/targets.js +36 -0
  22. package/build/esm/admin/factories.mjs +318 -0
  23. package/build/esm/admin/index.mjs +65 -0
  24. package/build/esm/api-version.mjs +18 -0
  25. package/build/esm/checkout/factories.mjs +413 -0
  26. package/build/esm/checkout/index.mjs +153 -0
  27. package/build/esm/customer-account/factories.mjs +231 -0
  28. package/build/esm/customer-account/index.mjs +83 -0
  29. package/build/esm/fetch-polyfills.mjs +188 -0
  30. package/build/esm/index.mjs +394 -0
  31. package/build/esm/mocks/i18n.mjs +17 -0
  32. package/build/esm/mocks/money.mjs +12 -0
  33. package/build/esm/mocks/signals.mjs +29 -0
  34. package/build/esm/mocks/target-apis.mjs +33 -0
  35. package/build/esm/navigation.mjs +51 -0
  36. package/build/esm/point-of-sale/factories.mjs +506 -0
  37. package/build/esm/point-of-sale/index.mjs +79 -0
  38. package/build/esm/targets.mjs +29 -0
  39. package/build/esnext/admin/factories.esnext +318 -0
  40. package/build/esnext/admin/index.esnext +65 -0
  41. package/build/esnext/api-version.esnext +18 -0
  42. package/build/esnext/checkout/factories.esnext +413 -0
  43. package/build/esnext/checkout/index.esnext +153 -0
  44. package/build/esnext/customer-account/factories.esnext +231 -0
  45. package/build/esnext/customer-account/index.esnext +83 -0
  46. package/build/esnext/fetch-polyfills.esnext +188 -0
  47. package/build/esnext/index.esnext +394 -0
  48. package/build/esnext/mocks/i18n.esnext +17 -0
  49. package/build/esnext/mocks/money.esnext +12 -0
  50. package/build/esnext/mocks/signals.esnext +29 -0
  51. package/build/esnext/mocks/target-apis.esnext +33 -0
  52. package/build/esnext/navigation.esnext +51 -0
  53. package/build/esnext/point-of-sale/factories.esnext +506 -0
  54. package/build/esnext/point-of-sale/index.esnext +79 -0
  55. package/build/esnext/targets.esnext +29 -0
  56. package/build/ts/admin/factories.d.ts +19 -0
  57. package/build/ts/admin/factories.d.ts.map +1 -0
  58. package/build/ts/admin/index.d.ts +31 -0
  59. package/build/ts/admin/index.d.ts.map +1 -0
  60. package/build/ts/api-version.d.ts +11 -0
  61. package/build/ts/api-version.d.ts.map +1 -0
  62. package/build/ts/checkout/factories.d.ts +10 -0
  63. package/build/ts/checkout/factories.d.ts.map +1 -0
  64. package/build/ts/checkout/index.d.ts +53 -0
  65. package/build/ts/checkout/index.d.ts.map +1 -0
  66. package/build/ts/customer-account/factories.d.ts +8 -0
  67. package/build/ts/customer-account/factories.d.ts.map +1 -0
  68. package/build/ts/customer-account/index.d.ts +21 -0
  69. package/build/ts/customer-account/index.d.ts.map +1 -0
  70. package/build/ts/fetch-polyfills.d.ts +19 -0
  71. package/build/ts/fetch-polyfills.d.ts.map +1 -0
  72. package/build/ts/index.d.ts +137 -0
  73. package/build/ts/index.d.ts.map +1 -0
  74. package/build/ts/mocks/i18n.d.ts +14 -0
  75. package/build/ts/mocks/i18n.d.ts.map +1 -0
  76. package/build/ts/mocks/money.d.ts +9 -0
  77. package/build/ts/mocks/money.d.ts.map +1 -0
  78. package/build/ts/mocks/signals.d.ts +19 -0
  79. package/build/ts/mocks/signals.d.ts.map +1 -0
  80. package/build/ts/mocks/target-apis.d.ts +15 -0
  81. package/build/ts/mocks/target-apis.d.ts.map +1 -0
  82. package/build/ts/navigation.d.ts +41 -0
  83. package/build/ts/navigation.d.ts.map +1 -0
  84. package/build/ts/point-of-sale/factories.d.ts +20 -0
  85. package/build/ts/point-of-sale/factories.d.ts.map +1 -0
  86. package/build/ts/point-of-sale/index.d.ts +30 -0
  87. package/build/ts/point-of-sale/index.d.ts.map +1 -0
  88. package/build/ts/targets.d.ts +27 -0
  89. package/build/ts/targets.d.ts.map +1 -0
  90. package/build/tsconfig.tsbuildinfo +1 -0
  91. package/checkout.esnext +1 -0
  92. package/checkout.js +1 -0
  93. package/checkout.mjs +1 -0
  94. package/customer-account.esnext +1 -0
  95. package/customer-account.js +1 -0
  96. package/customer-account.mjs +1 -0
  97. package/index.esnext +1 -0
  98. package/index.js +1 -0
  99. package/index.mjs +1 -0
  100. package/package.json +90 -0
  101. package/point-of-sale.esnext +1 -0
  102. package/point-of-sale.js +1 -0
  103. package/point-of-sale.mjs +1 -0
  104. package/src/admin/README.md +78 -0
  105. package/src/admin/factories.ts +380 -0
  106. package/src/admin/index.ts +85 -0
  107. package/src/api-version.ts +23 -0
  108. package/src/checkout/README.md +152 -0
  109. package/src/checkout/factories.ts +483 -0
  110. package/src/checkout/index.ts +174 -0
  111. package/src/customer-account/README.md +86 -0
  112. package/src/customer-account/factories.ts +272 -0
  113. package/src/customer-account/index.ts +90 -0
  114. package/src/fetch-polyfills.ts +222 -0
  115. package/src/index.ts +545 -0
  116. package/src/mocks/i18n.ts +15 -0
  117. package/src/mocks/money.ts +7 -0
  118. package/src/mocks/signals.ts +37 -0
  119. package/src/mocks/target-apis.ts +56 -0
  120. package/src/navigation.ts +72 -0
  121. package/src/point-of-sale/README.md +100 -0
  122. package/src/point-of-sale/factories.ts +581 -0
  123. package/src/point-of-sale/index.ts +92 -0
  124. package/src/targets.ts +68 -0
  125. package/src/tests/admin-factories.test.ts +31 -0
  126. package/src/tests/fetch.test.ts +139 -0
  127. package/src/tests/fixtures/test-module.ts +1 -0
  128. package/src/tests/getExtension.test.ts +208 -0
  129. package/src/tests/helpers.ts +140 -0
  130. package/src/tests/navigation.test.ts +130 -0
  131. package/src/tests/setUpExtension.test.ts +52 -0
package/README.md ADDED
@@ -0,0 +1,461 @@
1
+ # 🧪 @shopify/ui-extensions-tester
2
+
3
+ Write unit tests for [Shopify UI extensions](../ui-extensions) to ensure correctness and prevent regressions.
4
+
5
+ This testing library provides strongly typed mocks of the extension API--like the `shopify` global--so you can verify the correctness of your extension without needing a real Shopify host.
6
+
7
+ ## 📋 Requirements
8
+
9
+ - **API version `2025-10` or later** in your `shopify.extension.toml`
10
+ - **Node.js v20.20.0** or later
11
+ - **a mock DOM** such as [`environment: 'jsdom'`](https://vitest.dev/config/environment.html) in [`vitest`](https://vitest.dev/)
12
+ - **Test isolation** — extensions rely on the `shopify` global, so each test file must run in its own environment. We recommend [`vitest`](https://vitest.dev/) in [isolate mode](https://vitest.dev/config/isolate.html#isolate) (enabled by default).
13
+
14
+ ## 📋 Recommendations
15
+
16
+ - **TypeScript** — we recommend TypeScript to enforce API compliance against mock objects
17
+ - **Node.js ≥ 22.0.0** and **TypeScript ≥ 5.2** — to use ([Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management))
18
+ - **@testing-library/preact** — if your extension uses [Preact](https://preactjs.com/), we recommend installing [`@testing-library/preact`](https://preactjs.com/guide/v10/preact-testing-library/) for its `fireEvent` and `waitFor` helpers
19
+
20
+ ## 📦 Installation
21
+
22
+ Install the tester as a dev dependency alongside your preferred test runner:
23
+
24
+ ```bash
25
+ npm install --save-dev @shopify/ui-extensions-tester vitest
26
+ ```
27
+
28
+ If your extension renders with Preact, also install `@testing-library/preact`:
29
+
30
+ ```bash
31
+ npm install --save-dev @testing-library/preact
32
+ ```
33
+
34
+ ## 🏗️ Adding to an existing extension
35
+
36
+ If your extension was seeded from an older template using <a href="https://shopify.dev/docs/api/shopify-cli/app/app-generate-extension"><code>shopify app generate extension</code></a>, follow these steps.
37
+
38
+ <details>
39
+ <summary>Expand for details</summary>
40
+
41
+ The template gives you a project structure like this:
42
+
43
+ ```
44
+ my-app/
45
+ ├── extensions/
46
+ │ └── my-extension/
47
+ │ ├── src/
48
+ │ │ └── Checkout.jsx
49
+ │ ├── package.json
50
+ │ ├── shopify.d.ts
51
+ │ ├── shopify.extension.toml
52
+ │ └── tsconfig.json
53
+ ├── package.json
54
+ └── shopify.app.toml
55
+ ```
56
+
57
+ You need to add a few things:
58
+
59
+ ### 1. Add dependencies to the root `package.json`
60
+
61
+ Your extension's own `package.json` (inside `extensions/my-extension/`) already lists `@shopify/ui-extensions` for Shopify CLI. However, tests and typechecking run from the **root** project directory, so you also need `@shopify/ui-extensions` in the root `package.json` — otherwise TypeScript and vitest won't be able to resolve it.
62
+
63
+ ```jsonc
64
+ {
65
+ "scripts": {
66
+ // ...existing scripts
67
+ "test": "vitest run",
68
+ "typecheck": "tsc --noEmit --project extensions/my-extension/tsconfig.json"
69
+ },
70
+ "dependencies": {
71
+ "@shopify/ui-extensions": "latest"
72
+ },
73
+ "devDependencies": {
74
+ "@shopify/ui-extensions-tester": "latest",
75
+ "@testing-library/preact": "^3.2.0",
76
+ "typescript": "^5.2.0",
77
+ "vitest": "^3.0.0"
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### 2. Create `vitest.config.ts` at the project root
83
+
84
+ ```ts
85
+ import {defineConfig} from 'vitest/config';
86
+
87
+ export default defineConfig({
88
+ esbuild: {
89
+ jsx: 'automatic',
90
+ jsxImportSource: 'preact',
91
+ },
92
+ test: {
93
+ environment: 'jsdom',
94
+ },
95
+ });
96
+ ```
97
+
98
+ ### 3. Update the extension's `tsconfig.json`
99
+
100
+ Add the `tests` directory to the `include` array so your test files are typechecked:
101
+
102
+ ```jsonc
103
+ {
104
+ "compilerOptions": {
105
+ "jsx": "react-jsx",
106
+ "jsxImportSource": "preact",
107
+ "target": "ES2020",
108
+ "strict": true,
109
+ "checkJs": true,
110
+ "allowJs": true,
111
+ "moduleResolution": "node",
112
+ "esModuleInterop": true,
113
+ "skipLibCheck": true
114
+ },
115
+ "include": [
116
+ "./src",
117
+ "./tests",
118
+ "./shopify.d.ts"
119
+ ]
120
+ }
121
+ ```
122
+
123
+ ### 4. Create a `tests/` directory inside your extension
124
+
125
+ ```
126
+ extensions/
127
+ └── my-extension/
128
+ ├── src/
129
+ │ └── Checkout.jsx
130
+ └── tests/
131
+ └── Checkout.test.ts ← your tests go here
132
+ ```
133
+
134
+ ### 5. Add a triple-slash reference in each test file
135
+
136
+ Add a [triple-slash directive](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html) for your target so TypeScript knows about Shopify element types (`s-button`, `s-text`, etc.):
137
+
138
+ ```ts
139
+ /// <reference types="@shopify/ui-extensions/purchase.checkout.block.render" />
140
+ ```
141
+
142
+ The path must match the target you pass to `getExtension()`.
143
+
144
+ </details>
145
+
146
+ ## 🏊‍♀️ Getting started
147
+
148
+ Every test file follows the same pattern:
149
+ create an extension harness, set it up before
150
+ each test, and tear it down after.
151
+
152
+ ### Quick start with `using` (Node ≥ 22.0.0)
153
+
154
+ If your runtime supports
155
+ [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management),
156
+ use `setUpExtension` for zero-boilerplate
157
+ setup and automatic teardown:
158
+
159
+ ```ts
160
+ import {setUpExtension} from '@shopify/ui-extensions-tester';
161
+
162
+ test('rendering the extension', async () => {
163
+ using extension = setUpExtension(
164
+ 'purchase.checkout.block.render',
165
+ );
166
+ await extension.render();
167
+ // tearDown() is called automatically at the end of the block
168
+ });
169
+ ```
170
+
171
+ ### Classic setup
172
+
173
+ Alternatively, create the harness once and
174
+ manage the lifecycle with `beforeEach` /
175
+ `afterEach`:
176
+
177
+ ```ts
178
+ import {getExtension} from '@shopify/ui-extensions-tester';
179
+ import {beforeEach, afterEach} from 'vitest';
180
+
181
+ const extension = getExtension(
182
+ 'purchase.checkout.block.render',
183
+ );
184
+
185
+ beforeEach(() => {
186
+ extension.setUp();
187
+ });
188
+
189
+ afterEach(() => {
190
+ extension.tearDown();
191
+ });
192
+ ```
193
+
194
+ `setUp()` creates a complete mock `shopify` global compliant with the target. `tearDown()` clears the DOM and removes the global, among other surface-specific things.
195
+
196
+ ### 🔍 Rendering and querying elements
197
+
198
+ Call `extension.render()` to import and execute your extension's callback, then query the DOM with standard DOM APIs like [`document.body.querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) and [`document.body.querySelectorAll()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll):
199
+
200
+ ```ts
201
+ test('it handles an empty cart', async () => {
202
+ await extension.render();
203
+
204
+ const text =
205
+ document.body.querySelector('s-text')!;
206
+ expect(text.textContent).toEqual(
207
+ 'No items in cart',
208
+ );
209
+ });
210
+ ```
211
+
212
+ ### 🎨 Mocking shopify API values
213
+
214
+ The test setup will create a `shopify` global with sensible defaults for the target. You can mutate global property values on `extension.shopify` before rendering.
215
+
216
+ ```ts
217
+ test('it handles an empty order', async () => {
218
+ extension.shopify.order.value = undefined;
219
+
220
+ await extension.render();
221
+
222
+ const text =
223
+ document.body.querySelector('s-text')!;
224
+ expect(text.textContent).toEqual(
225
+ 'Order not found',
226
+ );
227
+ });
228
+ ```
229
+
230
+ ### 🖱️ Triggering events
231
+
232
+ To simulate how a user would interact with your UI extension, you can call [`dispatchEvent()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) or use `fireEvent` from `@testing-library/preact`. When an event triggers an async state change (like a Preact re-render), wrap follow-up assertions in `await waitFor()` to wait for the DOM to settle:
233
+
234
+ ```ts
235
+ import {
236
+ fireEvent,
237
+ waitFor,
238
+ } from '@testing-library/preact';
239
+
240
+ test('it handles date field changes', async () => {
241
+ await extension.render();
242
+
243
+ const dateField = document.body.querySelector(
244
+ 's-date-field',
245
+ )!;
246
+ dateField.value = '1990-05-20';
247
+ fireEvent.change(dateField);
248
+
249
+ const button =
250
+ document.body.querySelector('s-button')!;
251
+ fireEvent.click(button);
252
+
253
+ await waitFor(() => {
254
+ const banner =
255
+ document.body.querySelector('s-banner')!;
256
+ expect(banner.textContent).toEqual('Saved');
257
+ });
258
+ });
259
+ ```
260
+
261
+ ### 🔒 Safely mocking mutation functions
262
+
263
+ When mocking without strict typing, like with [`vitest` mocks](https://vitest.dev/api/vi.html#mocking-functions-and-objects), you can use a surface-specific `createResult()` helper to return type-safe values:
264
+
265
+ ```ts
266
+ import {createResult} from '@shopify/ui-extensions-tester/checkout';
267
+
268
+ const applyMetafieldChange = vi
269
+ .fn()
270
+ .mockResolvedValue(
271
+ createResult('applyMetafieldChange', {
272
+ type: 'error',
273
+ message:
274
+ 'Could not apply metafield changes',
275
+ }),
276
+ );
277
+ extension.shopify.applyMetafieldChange =
278
+ applyMetafieldChange;
279
+ ```
280
+
281
+ The first argument is the mutation API name. The second is an optional result override — omit it to get sensible defaults (like `{type: 'success'}`).
282
+
283
+ ### ⚡ Testing extension code that relies on signals
284
+
285
+ Extensions typically subscribe to signal-like objects such as [`shopify.lines.value`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/cart-lines#standardapi-propertydetail-lines).
286
+ The mock Shopify API **does not implement working signals** so you'll need to test each state change separately.
287
+
288
+ For example, let's say your extension has a button that updates the quantity of an item in the cart. Test the button first:
289
+
290
+ ```ts
291
+ import {createCartLine} from '@shopify/ui-extensions-tester/checkout';
292
+
293
+ test('increments cart line quantity on click', async () => {
294
+ const line = createCartLine();
295
+ extension.shopify.lines.value = [line];
296
+ const applyCartLinesChange = vi.spyOn(
297
+ extension.shopify,
298
+ 'applyCartLinesChange',
299
+ );
300
+
301
+ await extension.render();
302
+
303
+ const button =
304
+ document.body.querySelector('s-button')!;
305
+ fireEvent.click(button);
306
+
307
+ // Make sure clicking the button updates the quantity:
308
+ await waitFor(() => {
309
+ expect(
310
+ applyCartLinesChange,
311
+ ).toHaveBeenCalledWith({
312
+ type: 'updateCartLine',
313
+ id: line.id,
314
+ quantity: line.quantity + 1,
315
+ });
316
+ });
317
+ });
318
+ ```
319
+
320
+ Next, simulate the state change for updated quantities that would have happened on the checkout host:
321
+
322
+ ```ts
323
+ test('it renders the cart line quantity', async () => {
324
+ extension.shopify.lines.value = [
325
+ createCartLine({quantity: 2}),
326
+ ];
327
+
328
+ await extension.render();
329
+
330
+ const text =
331
+ document.body.querySelector('s-text')!;
332
+ expect(text.textContent).toContain('2');
333
+ });
334
+ ```
335
+
336
+ ### 🌐 Working with translations
337
+
338
+ The default `shopify.i18n.translate()` mock returns key names as-is to make assertions easier.
339
+
340
+ For example, if you render `shopify.i18n.translate('headings.orderNotFound')` in extension code, you can test by looking for the rendered key name:
341
+
342
+ ```ts
343
+ test('it renders a banner when the order does not exist', async () => {
344
+ extension.shopify.order.value = undefined;
345
+
346
+ await extension.render();
347
+
348
+ const banner =
349
+ document.body.querySelector('s-banner')!;
350
+ // Check for the translation key, not the actual translation:
351
+ expect(banner.getAttribute('heading')).toEqual(
352
+ 'headings.orderNotFound',
353
+ );
354
+ });
355
+ ```
356
+
357
+ ## ☯️ Surface-specific guides
358
+
359
+ Each surface exports some helpers:
360
+
361
+ - ⚙️ [Admin](./src/admin/README.md)
362
+ - 🛒 [Checkout](./src/checkout/README.md)
363
+ - 🛂 [Customer Account](./src/customer-account/README.md)
364
+ - 🛍️ [Point of Sale](./src/point-of-sale/README.md)
365
+
366
+ ## 📖 API reference
367
+
368
+ Exports from `@shopify/ui-extensions-tester`:
369
+
370
+ ### `setUpExtension(target, options?)`
371
+
372
+ Creates the extension harness for the target, calls `setUp()`,
373
+ and returns a **disposable** object. Use it
374
+ with the `using` keyword for automatic teardown:
375
+
376
+ ```ts
377
+ test('example', async () => {
378
+ using extension = setUpExtension(
379
+ 'purchase.checkout.block.render',
380
+ );
381
+ await extension.render();
382
+ // tearDown() called automatically
383
+ });
384
+ ```
385
+
386
+ It reads `shopify.extension.toml`, finds the module for the given target, and provides helpers to mock the environment and render the extension. It locates `shopify.extension.toml` by walking up from the calling test file's directory, and falls back to searching `extensions/` under the current working directory. Results are cached: calling `getExtension` twice with the same target and the same resolved TOML returns the same instance.
387
+
388
+ | Option | Type | Default | Description |
389
+ | ----------------- | -------- | ----------------------------- | ---------------------------------------------------------- |
390
+ | `configSearchDir` | `string` | calling test file's directory | Directory to start searching for `shopify.extension.toml`. |
391
+
392
+ By default, it walks up from the test file's directory to find `shopify.extension.toml`.
393
+
394
+ **Returns** an `Extension` object with the following members:
395
+
396
+ #### `extension.render()`
397
+
398
+ Imports and executes the extension module's default export, rendering the extension into `document.body`. Returns a `Promise<void>`.
399
+
400
+ #### `extension.shopify`
401
+
402
+ A mock `shopify` global, typed correctly for the target under test. You can mutate any property.
403
+
404
+ #### `extension.fetch`
405
+
406
+ A mock `fetch()` function installed as `globalThis.fetch` during `setUp()` and removed during `tearDown()`.
407
+
408
+ Override it with a mock to control responses:
409
+
410
+ ```ts
411
+ extension.fetch = vi
412
+ .fn()
413
+ .mockResolvedValue(
414
+ new Response(JSON.stringify({ok: true})),
415
+ );
416
+ ```
417
+
418
+ Assigning to `extension.fetch` also updates `globalThis.fetch`, so extension code that calls `fetch()` directly will use the mock.
419
+
420
+ #### `extension.navigation`
421
+
422
+ A mock [`Navigation`](https://developer.mozilla.org/en-US/docs/Web/API/Navigation) object installed as `globalThis.navigation` during `setUp()` and removed during `tearDown()`. Typed using the `Navigation` interface from `@shopify/ui-extensions/customer-account`.
423
+
424
+ Override its properties with mocks to control navigation behaviour:
425
+
426
+ ```ts
427
+ import {createNavigationHistoryEntry} from '@shopify/ui-extensions-tester';
428
+
429
+ extension.navigation.navigate = vi.fn();
430
+ extension.navigation.currentEntry =
431
+ createNavigationHistoryEntry({
432
+ url: '/cart',
433
+ state: {items: 3},
434
+ });
435
+ ```
436
+
437
+ Assigning to `extension.navigation` also updates `globalThis.navigation`, so extension code that calls `navigation.navigate()` directly will use the mock.
438
+
439
+ ### `getExtension(target, options?)`
440
+
441
+ > ⚠️ Prefer [`setUpExtension`](#setupextensiontarget-options) on Node ≥ 22.0.0. Use `getExtension` only if your runtime does not support [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management).
442
+
443
+ Accepts the same arguments as `setUpExtension`. You must call `extension.setUp()` and `extension.tearDown()` explicitly.
444
+
445
+ **Returns** an `Extension` object with the following additional members:
446
+
447
+ #### `extension.setUp()`
448
+
449
+ Sets up an extension environment for testing. Creates a mock `shopify` global with some defaults.
450
+
451
+ #### `extension.tearDown()`
452
+
453
+ Tears down the extension environment. Resets the `shopify` global and clears `document.body`.
454
+
455
+ ### `createNavigationHistoryEntry(options?)`
456
+
457
+ Creates a [`NavigationHistoryEntry`](https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry) for mocking `navigation.currentEntry` or other navigation values.
458
+
459
+ - `url` — URL of the history entry (default `''`)
460
+ - `key` — key of the history entry (default `''`)
461
+ - `state` — developer-defined state retrieved via `getState()` (default `undefined`). Each `getState()` call returns a structured clone, matching real browser behaviour.
package/admin.esnext ADDED
@@ -0,0 +1 @@
1
+ export * from "./build/esnext/admin/index.esnext";
package/admin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require("./build/cjs/admin/index.js");
package/admin.mjs ADDED
@@ -0,0 +1 @@
1
+ export * from "./build/esm/admin/index.mjs";