@nan0web/ui 1.12.1 → 1.12.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 CHANGED
@@ -250,6 +250,16 @@ const res = await ScenarioTest.run(ValidatedApp, [
250
250
  { field: 'code', value: '' } // Simulating empty response
251
251
  ])
252
252
  ```
253
+ ### Story Testing (.nan0 spec files)
254
+
255
+ The `SpecRunner.executeFile` helper allows running `.nan0` spec stories automatically without boilerplate DBFS setup.
256
+ All manual assertions are omitted because `SpecAdapter` handles strict expectation matching internally.
257
+
258
+ How to execute .nan0 spec files automatically?
259
+ ```js
260
+ import { SpecRunner } from '@nan0web/ui/testing'
261
+ const { SpecRunner } = await import('./testing/index.js')
262
+ ```
253
263
  All components, adapters, and models are designed to be testable
254
264
  with minimal setup.
255
265
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nan0web/ui",
3
- "version": "1.12.1",
3
+ "version": "1.12.2",
4
4
  "description": "NaN•Web UI. One application logic (algorithm) and many UI.",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -77,20 +77,20 @@
77
77
  "@nan0web/db-fs": "1.2.2",
78
78
  "@nan0web/event": "1.0.1",
79
79
  "@nan0web/i18n": "1.5.0",
80
- "@nan0web/icons": "1.1.0",
81
80
  "@nan0web/inspect": "1.0.0",
81
+ "@nan0web/test": "1.1.4",
82
82
  "@nan0web/ui-cli": "2.13.1",
83
- "@nan0web/nan0web.app": "0.1.0",
83
+ "@nan0web/release": "1.0.3",
84
+ "@nan0web/icons": "1.1.0",
84
85
  "@nan0web/ui-lit": "1.1.0",
85
- "@nan0web/test": "1.1.4",
86
- "@nan0web/release": "1.0.3"
86
+ "@nan0web/nan0web.app": "0.1.0"
87
87
  },
88
88
  "dependencies": {
89
89
  "string-width": "^7.2.0",
90
90
  "@nan0web/co": "2.0.1",
91
91
  "@nan0web/core": "1.1.3",
92
- "@nan0web/log": "1.1.1",
93
- "@nan0web/types": "1.7.3"
92
+ "@nan0web/types": "1.7.3",
93
+ "@nan0web/log": "1.1.1"
94
94
  },
95
95
  "scripts": {
96
96
  "prebuild": "rm -rf types/",
@@ -276,6 +276,14 @@ export async function runGenerator(generator, handlers, options = {}) {
276
276
  break
277
277
  }
278
278
 
279
+ case 'result': {
280
+ if (handlers.result) {
281
+ await handlers.result(intent)
282
+ }
283
+ nextVal = undefined
284
+ break
285
+ }
286
+
279
287
  default:
280
288
  throw IntentErrorModel.error('unhandled_intent', { type: /** @type {any} */ (intent).type })
281
289
  }
@@ -35,6 +35,43 @@ export class SpecRunner extends ModelAsApp {
35
35
  this.registry
36
36
  }
37
37
 
38
+ /**
39
+ * Convenience method to load a .nan0 file and run a specific scenario.
40
+ *
41
+ * 💡 Note on Expectations:
42
+ * You do NOT need to write manual assertions when using this method.
43
+ * The `for await (const _ of runner.run()) {}` loop drives the generator,
44
+ * but ALL assertions are handled automatically inside `SpecAdapter.js`.
45
+ *
46
+ * Whenever the App yields an intent (`ask`, `show`, `result`), `SpecAdapter`
47
+ * intercepts it and compares it strictly against the next step in the `.nan0` file.
48
+ * - If it matches, the test continues (and `$value` is injected back into the App).
49
+ * - If it mismatches, it throws an `assert.fail()` which fails the Node.js test immediately.
50
+ * - If the App finishes early, it throws an `unhandledSteps` error.
51
+ *
52
+ * @param {string} fileDir The directory containing the file (e.g., import.meta.dirname)
53
+ * @param {string} fileName The name of the .nan0 file
54
+ * @param {string} scenarioName The name of the scenario to run
55
+ * @param {Record<string, any>} registry The Model Class registry
56
+ * @param {Partial<import('../index.js').ModelAsAppOptions>} [options={}] Additional runner context options
57
+ * @throws {Error} If the scenario is missing or if expectations fail during execution
58
+ */
59
+ static async executeFile(fileDir, fileName, scenarioName, registry, options = {}) {
60
+ const DB = (await import('@nan0web/db-fs')).DBFS
61
+ const db = new DB({ root: fileDir })
62
+ const doc = await db.loadDocument(fileName)
63
+ const scenarios = Array.isArray(doc) ? doc : [doc]
64
+ const scenario = scenarios.find((s) => s.name === scenarioName) || scenarios[0]
65
+
66
+ if (!scenario) throw new Error(`Scenario ${scenarioName} not found in ${fileName}`)
67
+ if (!scenario.story) throw new Error(`Scenario ${scenarioName} has no story array`)
68
+
69
+ const runner = new this({ stream: scenario.story, registry }, options)
70
+ for await (const _ of runner.run()) {
71
+ // Iterate completely
72
+ }
73
+ }
74
+
38
75
  /**
39
76
  * @throws {Error}
40
77
  * @returns {AsyncGenerator<import('../core/Intent.js').Intent, import('../core/Intent.js').ResultIntent, any>}
@@ -16,6 +16,28 @@ export class SpecRunner extends ModelAsApp {
16
16
  running: string;
17
17
  unhandledSteps: string;
18
18
  };
19
+ /**
20
+ * Convenience method to load a .nan0 file and run a specific scenario.
21
+ *
22
+ * 💡 Note on Expectations:
23
+ * You do NOT need to write manual assertions when using this method.
24
+ * The `for await (const _ of runner.run()) {}` loop drives the generator,
25
+ * but ALL assertions are handled automatically inside `SpecAdapter.js`.
26
+ *
27
+ * Whenever the App yields an intent (`ask`, `show`, `result`), `SpecAdapter`
28
+ * intercepts it and compares it strictly against the next step in the `.nan0` file.
29
+ * - If it matches, the test continues (and `$value` is injected back into the App).
30
+ * - If it mismatches, it throws an `assert.fail()` which fails the Node.js test immediately.
31
+ * - If the App finishes early, it throws an `unhandledSteps` error.
32
+ *
33
+ * @param {string} fileDir The directory containing the file (e.g., import.meta.dirname)
34
+ * @param {string} fileName The name of the .nan0 file
35
+ * @param {string} scenarioName The name of the scenario to run
36
+ * @param {Record<string, any>} registry The Model Class registry
37
+ * @param {Partial<import('../index.js').ModelAsAppOptions>} [options={}] Additional runner context options
38
+ * @throws {Error} If the scenario is missing or if expectations fail during execution
39
+ */
40
+ static executeFile(fileDir: string, fileName: string, scenarioName: string, registry: Record<string, any>, options?: Partial<import("../index.js").ModelAsAppOptions>): Promise<void>;
19
41
  /**
20
42
  * Run a Nan0Spec sequence programmatically (for unit tests).
21
43
  *