create-outsystems-astro 0.9.0 → 0.11.0

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 (62) hide show
  1. package/bin/cli.js +42 -1
  2. package/integrations/.prettierignore +15 -0
  3. package/integrations/.yarn/releases/yarn-4.15.0.cjs +940 -0
  4. package/integrations/.yarnrc.yml +6 -0
  5. package/integrations/bun.lock +1250 -0
  6. package/integrations/bunfig.toml +3 -0
  7. package/integrations/deno.json +4 -0
  8. package/integrations/deno.lock +3943 -0
  9. package/integrations/eslint.config.mjs +61 -0
  10. package/integrations/html/client.ts +30 -0
  11. package/integrations/html/index.ts +57 -0
  12. package/integrations/html/server.ts +54 -0
  13. package/integrations/package-lock.json +8926 -0
  14. package/integrations/package.json +42 -0
  15. package/integrations/pnpm-lock.yaml +5562 -0
  16. package/integrations/pnpm-workspace.yaml +4 -0
  17. package/integrations/tsconfig.json +16 -0
  18. package/integrations/twig/client.ts +34 -0
  19. package/integrations/twig/index.ts +185 -0
  20. package/integrations/twig/server.ts +54 -0
  21. package/integrations/yarn.lock +6375 -0
  22. package/package.json +3 -1
  23. package/template/.github/workflows/deno-test.yml +1 -1
  24. package/template/.github/workflows/npm-test.yml +1 -1
  25. package/template/.github/workflows/pnpm-test.yml +2 -2
  26. package/template/.github/workflows/yarn-test.yml +26 -13
  27. package/template/.gitignore +4 -0
  28. package/template/.prettierignore +1 -0
  29. package/template/.yarn/releases/yarn-4.15.0.cjs +940 -0
  30. package/template/.yarnrc.yml +8 -0
  31. package/template/AGENTS.md +93 -1
  32. package/template/README.md +15 -0
  33. package/template/astro.config.mjs +8 -0
  34. package/template/bun.lock +281 -367
  35. package/template/bunfig.toml +3 -0
  36. package/template/deno.json +3 -11
  37. package/template/deno.lock +703 -676
  38. package/template/eslint.config.mjs +1 -0
  39. package/template/package-lock.json +705 -787
  40. package/template/package.json +38 -41
  41. package/template/pnpm-lock.yaml +1126 -1114
  42. package/template/pnpm-workspace.yaml +5 -0
  43. package/template/src/env.d.ts +6 -0
  44. package/template/src/framework/html/Demo.ts +105 -0
  45. package/template/src/framework/html/Store.ts +47 -0
  46. package/template/src/framework/twig/Demo.twig +100 -0
  47. package/template/src/framework/twig/Store.twig +45 -0
  48. package/template/src/images/html.png +0 -0
  49. package/template/src/images/twig.png +0 -0
  50. package/template/src/pages/html/html-demo.astro +61 -0
  51. package/template/src/pages/multi/store.astro +13 -1
  52. package/template/src/pages/twig/twig-demo.astro +65 -0
  53. package/template/src/stores/framework.ts +2 -0
  54. package/template/test/e2e/html/html-demo.spec.ts +36 -0
  55. package/template/test/e2e/twig/twig-demo.spec.ts +36 -0
  56. package/template/test/integration/html/Demo.test.ts +83 -0
  57. package/template/test/integration/twig/Demo.test.ts +84 -0
  58. package/template/tsconfig.json +1 -1
  59. package/template/vitest.config.ts +18 -0
  60. package/template/yarn.lock +14777 -10350
  61. /package/template/patches/{@analogjs+astro-angular+2.5.1.patch → @analogjs+astro-angular+2.5.2.patch} +0 -0
  62. /package/template/patches/{@angular+build+21.2.11.patch → @angular+build+21.2.12.patch} +0 -0
@@ -4,3 +4,8 @@ allowBuilds:
4
4
  lmdb: true
5
5
  msgpackr-extract: true
6
6
  sharp: true
7
+ minimumReleaseAge: 10080
8
+ minimumReleaseAgeExclude:
9
+ - tmp@0.2.6
10
+ overrides:
11
+ tmp: ^0.2.6
@@ -0,0 +1,6 @@
1
+ /// <reference types="astro/client" />
2
+
3
+ declare module "*.twig" {
4
+ const content: string;
5
+ export default content;
6
+ }
@@ -0,0 +1,105 @@
1
+ import AstroLogo from "../../images/astro.png?url";
2
+ import OutSystemsLogo from "../../images/outsystems.png?url";
3
+
4
+ interface DemoProps {
5
+ children?: string;
6
+ header?: string;
7
+ initialCount?: number;
8
+ showMessage?: string;
9
+ }
10
+
11
+ export default function Demo({
12
+ initialCount = 0,
13
+ showMessage = "",
14
+ }: DemoProps): string {
15
+ return `
16
+ <div class="counter-title" slot="header">HTML Demo Component</div>
17
+ <div class="html-demo">
18
+ <div class="card-grid">
19
+ <div class="card">
20
+ <strong>HTML counter component</strong>
21
+ <div class="card-content">
22
+ Internal counter controls. It keeps state within the component.
23
+ <div class="counter-controls">
24
+ <button class="subtract">-</button>
25
+ <pre class="count">${initialCount}</pre>
26
+ <button class="add">+</button>
27
+ </div>
28
+ </div>
29
+ The button sends the current count value to a function in the parent
30
+ component.
31
+ <div class="card-content">
32
+ <div>
33
+ <button class="card-btn send">Send value</button>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <div class="card">
38
+ <strong>Nano Stores</strong>
39
+ <div class="card-content">
40
+ <div>
41
+ <strong>Value:</strong>
42
+ <div class="nanostore-value"></div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ <div class="card unused">
47
+ <strong>Slot content (not supported)</strong>
48
+ <div class="card-content"></div>
49
+ </div>
50
+ </div>
51
+ <div class="counter-logos">
52
+ <img alt="OutSystems logo" src="${OutSystemsLogo}" />
53
+ <img alt="Astro logo" src="${AstroLogo}" />
54
+ </div>
55
+ <script>
56
+ (function () {
57
+ const container = (document.currentScript && document.currentScript.parentElement) || document.querySelector('.html-demo');
58
+ let count = ${initialCount};
59
+ const countEl = container.querySelector('.count');
60
+ const addBtn = container.querySelector('.add');
61
+ const subtractBtn = container.querySelector('.subtract');
62
+ const sendBtn = container.querySelector('.send');
63
+ const nanostoreEl = container.querySelector('.nanostore-value');
64
+
65
+ addBtn.addEventListener('click', function () {
66
+ count += 1;
67
+ countEl.textContent = count;
68
+ });
69
+
70
+ subtractBtn.addEventListener('click', function () {
71
+ count -= 1;
72
+ countEl.textContent = count;
73
+ });
74
+
75
+ sendBtn.addEventListener('click', function () {
76
+ if ('${showMessage}' && window['${showMessage}']) {
77
+ window['${showMessage}'](count);
78
+ }
79
+ });
80
+
81
+ if (!window.Stores) window.Stores = {};
82
+ if (!window.Stores['htmlStore']) {
83
+ let _value = 'Test Value';
84
+ const _subs = [];
85
+ window.Stores['htmlStore'] = {
86
+ get: function () { return _value; },
87
+ set: function (v) { _value = v; _subs.forEach(function (fn) { fn(v); }); },
88
+ subscribe: function (fn) {
89
+ fn(_value);
90
+ _subs.push(fn);
91
+ return function () { _subs.splice(_subs.indexOf(fn), 1); };
92
+ },
93
+ };
94
+ }
95
+
96
+ const store = window.Stores['htmlStore'];
97
+ nanostoreEl.textContent = store.get();
98
+ store.subscribe(function (value) {
99
+ nanostoreEl.textContent = value;
100
+ });
101
+ })();
102
+ </script>
103
+ </div>
104
+ `;
105
+ }
@@ -0,0 +1,47 @@
1
+ import HTMLLogo from "../../images/html.png?url";
2
+ import { framework } from "../../stores/framework";
3
+
4
+ if (typeof window !== "undefined") {
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ if (!(window as any).Stores) (window as any).Stores = {};
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ (window as any).Stores["framework"] = framework;
9
+ }
10
+
11
+ export default function Store(): string {
12
+ return `
13
+ <div class="card">
14
+ <strong>HTML Store</strong>
15
+ <div class="card-content">
16
+ <img alt="HTML logo" height="150" src="${HTMLLogo}" />
17
+ <div>
18
+ <strong>Value:</strong>
19
+ <div class="framework-value"></div>
20
+ </div>
21
+ <div>
22
+ <button class="card-btn select-btn">Select HTML</button>
23
+ </div>
24
+ </div>
25
+ <script>
26
+ (function () {
27
+ const container = (document.currentScript && document.currentScript.parentElement)
28
+ || document.querySelector('.html-store');
29
+ const valueEl = container.querySelector('.framework-value');
30
+ const btn = container.querySelector('.select-btn');
31
+
32
+ const store = window.Stores && window.Stores['framework'];
33
+
34
+ if (store) {
35
+ store.subscribe(function (value) {
36
+ valueEl.textContent = value;
37
+ });
38
+ }
39
+
40
+ btn.addEventListener('click', function () {
41
+ if (store) store.set('HTML');
42
+ });
43
+ })();
44
+ </script>
45
+ </div>
46
+ `;
47
+ }
@@ -0,0 +1,100 @@
1
+ {#
2
+ Twig counter demo component, authored as a native .twig file.
3
+
4
+ Available props (passed as island attributes and used as the Twig context):
5
+ - initialCount: starting counter value (default 0)
6
+ - showMessage: name of a window handler to call with the current count
7
+ - outSystemsLogo / astroLogo: image URLs, passed in from the .astro page
8
+ #}
9
+
10
+ {% set count = initialCount|default(0) %}
11
+ {% set handler = showMessage|default('') %}
12
+
13
+ <div class="counter-title">Twig Demo Component</div>
14
+ <div class="twig-demo">
15
+ <div class="card-grid">
16
+ <div class="card">
17
+ <strong>Twig counter component</strong>
18
+ <div class="card-content">
19
+ Internal counter controls. It keeps state within the component.
20
+ <div class="counter-controls">
21
+ <button class="subtract">-</button>
22
+ <pre class="count">{{ count }}</pre>
23
+ <button class="add">+</button>
24
+ </div>
25
+ </div>
26
+ The button sends the current count value to a function in the parent
27
+ component.
28
+ <div class="card-content">
29
+ <div>
30
+ <button class="card-btn send">Send value</button>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <div class="card">
35
+ <strong>Nano Stores</strong>
36
+ <div class="card-content">
37
+ <div>
38
+ <strong>Value:</strong>
39
+ <div class="nanostore-value"></div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <div class="card unused">
44
+ <strong>Slot content (not supported)</strong>
45
+ <div class="card-content"></div>
46
+ </div>
47
+ </div>
48
+ <div class="counter-logos">
49
+ {% if outSystemsLogo %}<img alt="OutSystems logo" src="{{ outSystemsLogo }}" />{% endif %}
50
+ {% if astroLogo %}<img alt="Astro logo" src="{{ astroLogo }}" />{% endif %}
51
+ </div>
52
+ <script>
53
+ (function () {
54
+ const container = (document.currentScript && document.currentScript.parentElement) || document.querySelector('.twig-demo');
55
+ let count = {{ count }};
56
+ const countEl = container.querySelector('.count');
57
+ const addBtn = container.querySelector('.add');
58
+ const subtractBtn = container.querySelector('.subtract');
59
+ const sendBtn = container.querySelector('.send');
60
+ const nanostoreEl = container.querySelector('.nanostore-value');
61
+
62
+ addBtn.addEventListener('click', function () {
63
+ count += 1;
64
+ countEl.textContent = count;
65
+ });
66
+
67
+ subtractBtn.addEventListener('click', function () {
68
+ count -= 1;
69
+ countEl.textContent = count;
70
+ });
71
+
72
+ sendBtn.addEventListener('click', function () {
73
+ if ('{{ handler }}' && window['{{ handler }}']) {
74
+ window['{{ handler }}'](count);
75
+ }
76
+ });
77
+
78
+ if (!window.Stores) window.Stores = {};
79
+ if (!window.Stores['twigStore']) {
80
+ let _value = 'Test Value';
81
+ const _subs = [];
82
+ window.Stores['twigStore'] = {
83
+ get: function () { return _value; },
84
+ set: function (v) { _value = v; _subs.forEach(function (fn) { fn(v); }); },
85
+ subscribe: function (fn) {
86
+ fn(_value);
87
+ _subs.push(fn);
88
+ return function () { _subs.splice(_subs.indexOf(fn), 1); };
89
+ },
90
+ };
91
+ }
92
+
93
+ const store = window.Stores['twigStore'];
94
+ nanostoreEl.textContent = store.get();
95
+ store.subscribe(function (value) {
96
+ nanostoreEl.textContent = value;
97
+ });
98
+ })();
99
+ </script>
100
+ </div>
@@ -0,0 +1,45 @@
1
+ {#
2
+ Twig store demo component, authored as a native .twig file.
3
+
4
+ Shares the `framework` Nano Store with the other framework islands through
5
+ `window.Stores['framework']`, which the page registers before the islands
6
+ hydrate. This lets a string-rendered Twig island stay in sync with the
7
+ binding-based islands (React, Vue, ...).
8
+
9
+ Props:
10
+ - logo: the Twig logo URL, passed in from the .astro page
11
+ #}
12
+
13
+ <div class="card twig-store">
14
+ <strong>Twig Store</strong>
15
+ <div class="card-content">
16
+ {% if logo %}<img alt="Twig logo" height="150" src="{{ logo }}" />{% endif %}
17
+ <div>
18
+ <strong>Value:</strong>
19
+ <div class="framework-value"></div>
20
+ </div>
21
+ <div>
22
+ <button class="card-btn select-btn">Select Twig</button>
23
+ </div>
24
+ </div>
25
+ <script>
26
+ (function () {
27
+ const container = (document.currentScript && document.currentScript.parentElement)
28
+ || document.querySelector('.twig-store');
29
+ const valueEl = container.querySelector('.framework-value');
30
+ const btn = container.querySelector('.select-btn');
31
+
32
+ const store = window.Stores && window.Stores['framework'];
33
+
34
+ if (store) {
35
+ store.subscribe(function (value) {
36
+ valueEl.textContent = value;
37
+ });
38
+ }
39
+
40
+ btn.addEventListener('click', function () {
41
+ if (store) store.set('Twig');
42
+ });
43
+ })();
44
+ </script>
45
+ </div>
Binary file
Binary file
@@ -0,0 +1,61 @@
1
+ ---
2
+ import DemoComponent from "../../framework/html/Demo";
3
+ import styles from "../../styles/index.css?url";
4
+ const initialCount = 5;
5
+ const showMessage = "showMessage";
6
+ ---
7
+ <html lang="en">
8
+ <head>
9
+ <link href={styles} rel="stylesheet" />
10
+ <script>
11
+ import { setupStore } from "../../stores/demo";
12
+ setupStore("htmlStore");
13
+ window["showMessage"] = (count) => {
14
+ document.getElementById("counter").textContent = count;
15
+ };
16
+ document.addEventListener("DOMContentLoaded", function () {
17
+ const input = document.getElementById("store");
18
+ input.value = window.Stores["htmlStore"].get();
19
+ });
20
+ </script>
21
+ </head>
22
+ <body>
23
+ <div class="counter-title">Astro Page</div>
24
+ <div class="card-grid">
25
+ <div class="card">
26
+ <strong>Counter component</strong>
27
+ <div class="card-content">
28
+ <div style="text-align: center; margin-top: 30px;">
29
+ Counter value:
30
+ <div>
31
+ <span id="counter"></span>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <div class="card">
37
+ <strong>Nano Stores</strong>
38
+ <div class="card-content">
39
+ <div style="text-align: center; margin-top: 30px;">
40
+ <input
41
+ id="store"
42
+ placeholder="Type something..."
43
+ type="text"
44
+ /><button
45
+ class="card-btn"
46
+ onclick="window.Stores['htmlStore'].set(document.getElementById('store').value)"
47
+ >Update Store</button
48
+ >
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ <hr />
54
+ <DemoComponent
55
+ client:load
56
+ initialCount={initialCount}
57
+ showMessage={showMessage}
58
+ >
59
+ </DemoComponent>
60
+ </body>
61
+ </html>
@@ -1,23 +1,35 @@
1
1
  ---
2
- import AngularStore from "../../framework/angular/Store.component"
2
+ import AngularStore from "../../framework/angular/Store.component";
3
+ import HTMLStore from "../../framework/html/Store";
3
4
  import PreactStore from "../../framework/preact/Store";
4
5
  import ReactStore from "../../framework/react/Store";
5
6
  import SolidStore from "../../framework/solid/Store";
6
7
  import SvelteStore from "../../framework/svelte/Store.svelte";
8
+ import TwigStore from "../../framework/twig/Store.twig";
7
9
  import VueStore from "../../framework/vue/Store.vue";
10
+ import TwigLogo from "../../images/twig.png?url";
8
11
  import styles from "../../styles/index.css?url";
9
12
  ---
10
13
  <html lang="en">
11
14
  <head>
12
15
  <link href={styles} rel="stylesheet" />
16
+ <script>
17
+ // Expose the shared framework Nano Store so string-rendered islands
18
+ // (such as the Twig store) can subscribe to it before they hydrate.
19
+ import { framework } from "../../stores/framework";
20
+ window.Stores = window.Stores || {};
21
+ window.Stores["framework"] = framework;
22
+ </script>
13
23
  </head>
14
24
  <body>
15
25
  <div class="card-grid">
16
26
  <AngularStore client:load />
27
+ <HTMLStore client:load />
17
28
  <PreactStore client:only="preact" />
18
29
  <ReactStore client:only="react" />
19
30
  <SolidStore client:only="solid-js" />
20
31
  <SvelteStore client:only="svelte" />
32
+ <TwigStore client:load logo={TwigLogo} />
21
33
  <VueStore client:only="vue" />
22
34
  </div>
23
35
  </body>
@@ -0,0 +1,65 @@
1
+ ---
2
+ import AstroLogo from "../../images/astro.png?url";
3
+ import OutSystemsLogo from "../../images/outsystems.png?url";
4
+ import styles from "../../styles/index.css?url";
5
+ import DemoComponent from "../../framework/twig/Demo.twig";
6
+ const initialCount = 5;
7
+ const showMessage = "showMessage";
8
+ ---
9
+ <html lang="en">
10
+ <head>
11
+ <link href={styles} rel="stylesheet" />
12
+ <script>
13
+ import { setupStore } from "../../stores/demo";
14
+ setupStore("twigStore");
15
+ window["showMessage"] = (count) => {
16
+ document.getElementById("counter").textContent = count;
17
+ };
18
+ document.addEventListener("DOMContentLoaded", function () {
19
+ const input = document.getElementById("store");
20
+ input.value = window.Stores["twigStore"].get();
21
+ });
22
+ </script>
23
+ </head>
24
+ <body>
25
+ <div class="counter-title">Astro Page</div>
26
+ <div class="card-grid">
27
+ <div class="card">
28
+ <strong>Counter component</strong>
29
+ <div class="card-content">
30
+ <div style="text-align: center; margin-top: 30px;">
31
+ Counter value:
32
+ <div>
33
+ <span id="counter"></span>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <div class="card">
39
+ <strong>Nano Stores</strong>
40
+ <div class="card-content">
41
+ <div style="text-align: center; margin-top: 30px;">
42
+ <input
43
+ id="store"
44
+ placeholder="Type something..."
45
+ type="text"
46
+ /><button
47
+ class="card-btn"
48
+ onclick="window.Stores['twigStore'].set(document.getElementById('store').value)"
49
+ >Update Store</button
50
+ >
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ <hr />
56
+ <DemoComponent
57
+ astroLogo={AstroLogo}
58
+ client:load
59
+ initialCount={initialCount}
60
+ outSystemsLogo={OutSystemsLogo}
61
+ showMessage={showMessage}
62
+ >
63
+ </DemoComponent>
64
+ </body>
65
+ </html>
@@ -2,10 +2,12 @@ import { atom } from "nanostores";
2
2
 
3
3
  export type CurrentSelectedFramework =
4
4
  | ""
5
+ | "HTML"
5
6
  | "Preact"
6
7
  | "React"
7
8
  | "Solid"
8
9
  | "Svelte"
10
+ | "Twig"
9
11
  | "Vue";
10
12
 
11
13
  export const framework = atom<CurrentSelectedFramework>("");
@@ -0,0 +1,36 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ test.beforeEach(async ({ page }) => {
4
+ await page.goto("/html/html-demo");
5
+ });
6
+
7
+ test.describe("Has values", () => {
8
+ test("Should have header", async ({ page }) => {
9
+ await expect(page.getByText("HTML Demo Component")).toBeVisible();
10
+ });
11
+ });
12
+
13
+ test.describe("Change counter", () => {
14
+ test("Should increment counter when clicking + button", async ({ page }) => {
15
+ await page.getByRole("button", { name: "+" }).click();
16
+ await expect(page.locator("pre")).toContainText("6");
17
+ });
18
+
19
+ test("Should decrement counter when clicking - button", async ({ page }) => {
20
+ await page.getByRole("button", { name: "-" }).click();
21
+ await expect(page.locator("pre")).toContainText("4");
22
+ });
23
+
24
+ test("Should show message on Send value", async ({ page }) => {
25
+ await page.getByRole("button", { name: "Send value" }).click();
26
+ await expect(page.locator("span#counter")).toContainText("5");
27
+ });
28
+ });
29
+
30
+ test.describe("Update Nano Store", () => {
31
+ test("Should update Nano Store", async ({ page }) => {
32
+ await page.locator("#store").fill("Updated Value");
33
+ await page.getByRole("button", { name: "Update Store" }).click();
34
+ await expect(page.locator(".nanostore-value")).toHaveText("Updated Value");
35
+ });
36
+ });
@@ -0,0 +1,36 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ test.beforeEach(async ({ page }) => {
4
+ await page.goto("/twig/twig-demo");
5
+ });
6
+
7
+ test.describe("Has values", () => {
8
+ test("Should have header", async ({ page }) => {
9
+ await expect(page.getByText("Twig Demo Component")).toBeVisible();
10
+ });
11
+ });
12
+
13
+ test.describe("Change counter", () => {
14
+ test("Should increment counter when clicking + button", async ({ page }) => {
15
+ await page.getByRole("button", { name: "+" }).click();
16
+ await expect(page.locator("pre")).toContainText("6");
17
+ });
18
+
19
+ test("Should decrement counter when clicking - button", async ({ page }) => {
20
+ await page.getByRole("button", { name: "-" }).click();
21
+ await expect(page.locator("pre")).toContainText("4");
22
+ });
23
+
24
+ test("Should show message on Send value", async ({ page }) => {
25
+ await page.getByRole("button", { name: "Send value" }).click();
26
+ await expect(page.locator("span#counter")).toContainText("5");
27
+ });
28
+ });
29
+
30
+ test.describe("Update Nano Store", () => {
31
+ test("Should update Nano Store", async ({ page }) => {
32
+ await page.locator("#store").fill("Updated Value");
33
+ await page.getByRole("button", { name: "Update Store" }).click();
34
+ await expect(page.locator(".nanostore-value")).toHaveText("Updated Value");
35
+ });
36
+ });
@@ -0,0 +1,83 @@
1
+ import { fireEvent, screen } from "@testing-library/dom";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ import Demo from "../../../src/framework/html/Demo";
5
+
6
+ function renderDemo(props: Parameters<typeof Demo>[0] = {}) {
7
+ document.body.innerHTML = Demo(props);
8
+ document.body.querySelectorAll("script").forEach((script) => {
9
+ new Function(script.textContent ?? "")();
10
+ });
11
+ }
12
+
13
+ describe("Demo", () => {
14
+ let capturedListener: ((val: string) => void) | undefined;
15
+ let storeValue = "Mocked Nano Value";
16
+
17
+ beforeEach(() => {
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ (window as any).mockFunction = vi.fn();
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ (window as any).Stores = {
23
+ htmlStore: {
24
+ get: vi.fn(() => storeValue),
25
+ set: vi.fn((v: string) => {
26
+ storeValue = v;
27
+ capturedListener?.(v);
28
+ }),
29
+ subscribe: vi.fn((fn: (val: string) => void) => {
30
+ capturedListener = fn;
31
+ fn(storeValue);
32
+ return () => {};
33
+ }),
34
+ },
35
+ };
36
+ });
37
+
38
+ afterEach(() => {
39
+ vi.clearAllMocks();
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ delete (window as any).mockFunction;
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ delete (window as any).Stores;
44
+ capturedListener = undefined;
45
+ storeValue = "Mocked Nano Value";
46
+ });
47
+
48
+ it("renders the initial count", () => {
49
+ renderDemo({ initialCount: 5 });
50
+ expect(screen.getByText("5")).toBeInTheDocument();
51
+ });
52
+
53
+ it("increments count when add button is clicked", () => {
54
+ renderDemo({ initialCount: 5 });
55
+ fireEvent.click(screen.getByRole("button", { name: "+" }));
56
+ expect(screen.getByText("6")).toBeInTheDocument();
57
+ });
58
+
59
+ it("decrements count when subtract button is clicked", () => {
60
+ renderDemo({ initialCount: 5 });
61
+ fireEvent.click(screen.getByRole("button", { name: "-" }));
62
+ expect(screen.getByText("4")).toBeInTheDocument();
63
+ });
64
+
65
+ it("calls the show message function with the current count", () => {
66
+ renderDemo({ initialCount: 5, showMessage: "mockFunction" });
67
+ fireEvent.click(screen.getByRole("button", { name: "Send value" }));
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
+ expect((window as any).mockFunction).toHaveBeenCalledWith(5);
70
+ });
71
+
72
+ it("displays the initial nanostore value", () => {
73
+ renderDemo({});
74
+ expect(screen.getByText("Mocked Nano Value")).toBeInTheDocument();
75
+ });
76
+
77
+ it("updates the display when the nanostore value changes", () => {
78
+ renderDemo({});
79
+ expect(screen.getByText("Mocked Nano Value")).toBeInTheDocument();
80
+ capturedListener?.("Updated Value");
81
+ expect(screen.getByText("Updated Value")).toBeInTheDocument();
82
+ });
83
+ });