@nan0web/ui 1.0.0 → 1.0.1

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.
@@ -1,77 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import stringWidth from "string-width"
4
- import View from "./View.js"
5
- import Frame from "../Frame/Frame.js"
6
- import FrameProps from "../Frame/Props.js"
7
- import Welcome from "../Component/Welcome/index.js"
8
-
9
- describe("View", () => {
10
- it("should create empty instance", () => {
11
- const view = new View()
12
- assert.ok(view)
13
- })
14
- it("should print frame", () => {
15
- const view = new View()
16
- view.render(1)(["Hello"])
17
- assert.equal(String(view.frame), Frame.CLEAR_LINE + "\r" + "Hello")
18
- const value = view.render(0)(["No output"])
19
- assert.deepStrictEqual({ ...value }, {
20
- imprint: Frame.CLEAR_LINE + "\r" + "No output",
21
- value: [["No output"]],
22
- width: 144,
23
- height: 33,
24
- renderMethod: "visible",
25
- defaultProps: new FrameProps(),
26
- })
27
- view.render(() => (["fn result"]))(["Even with a function"])
28
- assert.equal(String(view.frame), Frame.CLEAR_LINE + "\r" + "fn result")
29
- assert.deepStrictEqual(view.stdout.stream, [
30
- Frame.RESET + Frame.CLEAR_LINE + "\r" + "Hello",
31
- Frame.CLEAR_LINE + "\r" + "fn result"
32
- ])
33
- })
34
- it("should print frame with render method set to append", () => {
35
- const view = new View({ renderMethod: View.RenderMethod.APPEND })
36
- view.render(1)(["Hello"])
37
- assert.equal(String(view.frame), "Hello" + " ".repeat(139))
38
- view.render(1)(["world"])
39
- assert.equal(String(view.frame), "world" + " ".repeat(139))
40
- const value = view.render(0)(["No output"])
41
- assert.deepStrictEqual({ ...value }, {
42
- imprint: "No output" + " ".repeat(144 - "No output".length),
43
- value: [["No output"]],
44
- width: 144,
45
- height: 33,
46
- renderMethod: View.RenderMethod.APPEND,
47
- defaultProps: new FrameProps(),
48
- })
49
- view.render(() => (["fn result"]))(["Even with a function"])
50
- assert.equal(String(view.frame), "fn result" + " ".repeat(144 - 9))
51
- assert.deepStrictEqual(view.stdout.stream, [
52
- Frame.RESET + "Hello",
53
- Frame.RESET + "world",
54
- "fn result"
55
- ].map(
56
- row => row + " ".repeat(144 - stringWidth(row))
57
- ))
58
- })
59
- it("should render Welcome component", () => {
60
- const view = new View({
61
- renderMethod: View.RenderMethod.APPEND,
62
- width: 144,
63
- height: 33,
64
- })
65
- view.register(Welcome)
66
- const frame = view.render("Welcome")({ user: { name: "View" } })
67
- assert.ok(frame instanceof Frame)
68
- const expected = [
69
- "Welcome View!",
70
- "What can we do today great?",
71
- "",
72
- ].map(
73
- row => (row + " ".repeat(144 - row.length))
74
- ).join("\n")
75
- assert.deepStrictEqual(view.stdout.stream, [expected])
76
- })
77
- })
@@ -1,116 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import UIForm from "./Form.js"
4
- import FormInput from "./Input.js"
5
-
6
- describe("UIForm", () => {
7
- it("should create instance with default values", () => {
8
- const form = new UIForm()
9
- assert.ok(form instanceof UIForm)
10
- assert.equal(form.title, "")
11
- assert.deepEqual(form.fields, [])
12
- assert.deepEqual(form.state, {})
13
- })
14
-
15
- it("should create instance with custom values", () => {
16
- const fields = [
17
- new FormInput({ name: "name", label: "Name", required: true }),
18
- new FormInput({ name: "email", label: "Email", type: "email" })
19
- ]
20
- const props = {
21
- title: "Test Form",
22
- fields,
23
- state: { name: "John" }
24
- }
25
- const form = new UIForm(props)
26
- assert.equal(form.title, "Test Form")
27
- assert.equal(form.fields.length, 2)
28
- assert.deepEqual(form.state, { name: "John" })
29
- })
30
-
31
- it("should set data", () => {
32
- const form = new UIForm({ state: { name: "John" } })
33
- const newForm = form.setData({ email: "john@example.com" })
34
- assert.deepEqual(newForm.state, { name: "John", email: "john@example.com" })
35
- })
36
-
37
- it("should get field by name", () => {
38
- const fields = [new FormInput({ name: "test", label: "Test Field" })]
39
- const form = new UIForm({ fields })
40
- const field = form.getField("test")
41
- assert.ok(field)
42
- assert.equal(field.name, "test")
43
- })
44
-
45
- it("should get values", () => {
46
- const form = new UIForm({ state: { name: "John", age: 30 } })
47
- const values = form.getValues()
48
- assert.deepEqual(values, { name: "John", age: 30 })
49
- })
50
-
51
- it("should validate form", () => {
52
- const fields = [new FormInput({ name: "requiredField", required: true })]
53
- const form = new UIForm({ fields, state: {} })
54
- const result = form.validate()
55
- assert.ok(!result.isValid)
56
- assert.ok(result.errors.requiredField)
57
- })
58
-
59
- it("should validate individual field", () => {
60
- const fields = [new FormInput({ name: "email", type: "email" })]
61
- const form = new UIForm({ fields })
62
- const result = form.validateField("email", "invalid-email")
63
- assert.ok(!result.isValid)
64
- assert.ok(result.errors.email)
65
- })
66
-
67
- it("should convert to JSON", () => {
68
- const form = new UIForm({ title: "Test", state: { name: "John" } })
69
- const json = form.toJSON()
70
- assert.ok(json.id)
71
- assert.equal(json.title, "Test")
72
- assert.ok(Array.isArray(json.fields))
73
- assert.ok(json.state)
74
- assert.ok(json.meta)
75
- })
76
-
77
- it("parses default text fields", () => {
78
- const data = { name: "", email: "" }
79
- const { fields } = UIForm.parse(data)
80
- assert.equal(fields.length, 2)
81
- assert.ok(fields[0] instanceof FormInput)
82
- assert.equal(fields[0].name, "name")
83
- assert.equal(fields[0].label, "Name")
84
- assert.equal(fields[0].type, FormInput.TYPES.TEXT)
85
- assert.equal(fields[0].required, false)
86
- })
87
-
88
- it("applies overrides correctly", () => {
89
- const data = { age: "" }
90
- const { fields } = UIForm.parse(data, {
91
- age: { type: FormInput.TYPES.NUMBER, required: true, label: "User Age" },
92
- })
93
- const ageField = fields[0]
94
- assert.equal(ageField.type, FormInput.TYPES.NUMBER)
95
- assert.ok(ageField.required)
96
- assert.equal(ageField.label, "User Age")
97
- })
98
-
99
- it("supports static custom validators", () => {
100
- // register a validator that ensures a string contains only digits
101
- UIForm.addValidator("digitsOnly", value => {
102
- return /^\d+$/.test(value) ? true : "Must contain only digits"
103
- })
104
-
105
- const fields = [
106
- new FormInput({ name: "phone", label: "Phone", required: true })
107
- ]
108
- const schema = {
109
- phone: { validator: "digitsOnly" }
110
- }
111
- const form = new UIForm({ fields, schema, state: { phone: "12a34" } })
112
- const result = form.validate()
113
- assert.ok(!result.isValid)
114
- assert.equal(result.errors.phone, "Must contain only digits")
115
- })
116
- })
@@ -1,58 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import FormInput from "./Input.js"
4
-
5
- describe("FormInput", () => {
6
- it("should create instance with default values", () => {
7
- const input = new FormInput({ name: "name" })
8
- assert.ok(input instanceof FormInput)
9
- assert.equal(input.type, FormInput.TYPES.TEXT)
10
- assert.equal(input.required, false)
11
- assert.equal(input.name, "name")
12
- assert.equal(input.placeholder, "")
13
- assert.deepEqual(input.options, [])
14
- })
15
-
16
- it("should create instance with custom values", () => {
17
- const props = {
18
- type: "email",
19
- name: "email",
20
- label: "Email Address",
21
- required: true,
22
- placeholder: "Enter email",
23
- options: ["option1", "option2"],
24
- defaultValue: "test@example.com"
25
- }
26
- const input = new FormInput(props)
27
- assert.equal(input.type, "email")
28
- assert.equal(input.name, "email")
29
- assert.equal(input.label, "Email Address")
30
- assert.equal(input.required, true)
31
- assert.equal(input.placeholder, "Enter email")
32
- assert.deepEqual(input.options, ["option1", "option2"])
33
- assert.equal(input.defaultValue, "test@example.com")
34
- })
35
-
36
- it("should create from string", () => {
37
- const input = FormInput.from("testField")
38
- assert.equal(input.name, "testField")
39
- assert.equal(input.label, "testField")
40
- })
41
-
42
- it("should create from object", () => {
43
- const objInput = { name: "test", type: "text" }
44
- const input = FormInput.from(objInput)
45
- assert.ok(input instanceof FormInput)
46
- assert.equal(input.name, "test")
47
- })
48
-
49
- it("should validate type", async () => {
50
- const validInput = new FormInput({ name: "email", type: "email" })
51
- assert.ok(validInput)
52
- const fn = async () => new FormInput({ name: "email", type: "invalid" })
53
- await assert.rejects(fn, {
54
- name: "TypeError",
55
- message: /FormInput\.type is invalid!/
56
- })
57
- })
58
- })
@@ -1,54 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import FormMessage from "./Message.js"
4
-
5
- describe("FormMessage", () => {
6
- it("should create instance with default values", () => {
7
- const msg = new FormMessage()
8
- assert.ok(msg instanceof FormMessage)
9
- assert.deepEqual(msg.data, {})
10
- assert.deepEqual(msg.schema, {})
11
- assert.equal(msg.type, "form")
12
- })
13
-
14
- it("should create instance with custom values", () => {
15
- const props = {
16
- data: { name: "John" },
17
- schema: { name: { required: true } },
18
- body: ["Form content"]
19
- }
20
- const msg = new FormMessage(props)
21
- assert.deepEqual(msg.data, { name: "John" })
22
- assert.deepEqual(msg.schema, { name: { required: true } })
23
- assert.deepEqual(msg.body, ["Form content"])
24
- })
25
-
26
- it("should add data", () => {
27
- const msg = new FormMessage({ data: { name: "John" } })
28
- const newMsg = msg.addData({ age: 30 })
29
- assert.deepEqual(newMsg.data, { name: "John", age: 30 })
30
- })
31
-
32
- it("should validate data correctly", () => {
33
- const schema = {
34
- name: { required: true },
35
- email: { type: "email" },
36
- age: { type: "number" }
37
- }
38
- const msg = new FormMessage({ schema })
39
-
40
- // Valid data
41
- const validData = { name: "John", email: "john@example.com", age: "30" }
42
- const validResult = msg.validateData(validData)
43
- assert.ok(validResult.isValid)
44
- assert.deepEqual(validResult.errors, {})
45
-
46
- // Invalid data
47
- const invalidData = { email: "invalid-email", age: "not-a-number" }
48
- const invalidResult = msg.validateData(invalidData)
49
- assert.ok(!invalidResult.isValid)
50
- assert.ok(invalidResult.errors.name)
51
- assert.ok(invalidResult.errors.email)
52
- assert.ok(invalidResult.errors.age)
53
- })
54
- })
@@ -1,35 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import InputAdapter from "./InputAdapter.js"
4
-
5
- describe("InputAdapter", () => {
6
- it("should create instance", () => {
7
- const adapter = new InputAdapter()
8
- assert.ok(adapter instanceof InputAdapter)
9
- })
10
-
11
- it("should start and emit input message", () => {
12
- const adapter = new InputAdapter()
13
- let emitted = false
14
- let message = null
15
-
16
- adapter.on('input', (msg) => {
17
- emitted = true
18
- message = msg
19
- })
20
-
21
- adapter.start()
22
- assert.ok(emitted)
23
- assert.ok(message)
24
- })
25
-
26
- it("should stop without error", () => {
27
- const adapter = new InputAdapter()
28
- assert.doesNotThrow(() => adapter.stop())
29
- })
30
-
31
- it("should be ready by default", () => {
32
- const adapter = new InputAdapter()
33
- assert.ok(adapter.isReady())
34
- })
35
- })
@@ -1,45 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import InputMessage from "./InputMessage.js"
4
- import { notEmpty, empty } from "@nan0web/types"
5
-
6
- describe("InputMessage", () => {
7
- it("should create instance with default values", () => {
8
- const msg = new InputMessage()
9
- assert.ok(msg instanceof InputMessage)
10
- assert.equal(msg.value, "")
11
- assert.equal(msg.waiting, false)
12
- assert.deepEqual(msg.options, [])
13
- })
14
-
15
- it("should create instance with custom values", () => {
16
- const props = {
17
- value: "user input",
18
- waiting: true,
19
- options: ["option1", "option2"]
20
- }
21
- const msg = new InputMessage(props)
22
- assert.equal(msg.value, "user input")
23
- assert.equal(msg.waiting, true)
24
- assert.deepEqual(msg.options, ["option1", "option2"])
25
- })
26
-
27
- it("should handle string options correctly", () => {
28
- const msg = new InputMessage({ options: "single-option" })
29
- assert.deepEqual(msg.options, ["single-option"])
30
- })
31
-
32
- it("should detect empty value", () => {
33
- const emptyMsg = new InputMessage({ value: "" })
34
- const nonEmptyMsg = new InputMessage({ value: "test" })
35
- assert.ok(emptyMsg.empty)
36
- assert.ok(!nonEmptyMsg.empty)
37
- })
38
-
39
- it("should validate message", () => {
40
- const validMsg = new InputMessage({ value: "test" })
41
- const invalidMsg = new InputMessage({ value: "" })
42
- assert.ok(validMsg.isValid)
43
- assert.ok(!invalidMsg.isValid)
44
- })
45
- })
@@ -1,58 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import UIMessage from "./Message.js"
4
-
5
- describe("UIMessage", () => {
6
- it("should create instance with default values", () => {
7
- const msg = new UIMessage()
8
- assert.ok(msg instanceof UIMessage)
9
- assert.equal(typeof msg.type, "string")
10
- assert.equal(typeof msg.id, "string")
11
- assert.ok(msg.time instanceof Date)
12
- })
13
-
14
- it("should create instance with custom values", () => {
15
- const props = {
16
- type: "test",
17
- id: "custom-id",
18
- body: ["test content"]
19
- }
20
- const msg = new UIMessage(props)
21
- assert.equal(msg.type, "test")
22
- assert.equal(msg.id, "custom-id")
23
- assert.deepEqual(msg.body, ["test content"])
24
- })
25
-
26
- it("should generate unique id when not provided", () => {
27
- const msg1 = new UIMessage()
28
- const msg2 = new UIMessage()
29
- assert.notEqual(msg1.id, msg2.id)
30
- })
31
-
32
- it("should use content as body when body not provided", () => {
33
- const msg = new UIMessage({ content: ["hello"] })
34
- assert.deepEqual(msg.body, ["hello"])
35
- })
36
-
37
- it("should create from data using from() method", () => {
38
- const data = { type: "info", body: ["test"] }
39
- const msg = UIMessage.from(data)
40
- assert.ok(msg instanceof UIMessage)
41
- assert.equal(msg.type, "info")
42
- assert.deepEqual(msg.body, ["test"])
43
- })
44
-
45
- it("should validate type correctly", () => {
46
- const validMsg = new UIMessage({ type: UIMessage.TYPES.TEXT })
47
- const invalidMsg = new UIMessage({ type: "invalid-type" })
48
- assert.ok(validMsg.isValidType())
49
- assert.ok(!invalidMsg.isValidType())
50
- })
51
-
52
- it("should check if message is empty", () => {
53
- const emptyMsg = new UIMessage()
54
- const nonEmptyMsg = new UIMessage({ body: ["content"] })
55
- assert.ok(emptyMsg.isEmpty())
56
- assert.ok(!nonEmptyMsg.isEmpty())
57
- })
58
- })
@@ -1,61 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import OutputMessage from "./OutputMessage.js"
4
-
5
- describe("OutputMessage", () => {
6
- it("should create instance with default values", () => {
7
- const msg = new OutputMessage()
8
- assert.ok(msg instanceof OutputMessage)
9
- assert.deepEqual(msg.body, [])
10
- assert.deepEqual(msg.meta, {})
11
- assert.equal(msg.error, null)
12
- assert.equal(msg.priority, OutputMessage.PRIORITY.NORMAL)
13
- })
14
-
15
- it("should create instance with custom values", () => {
16
- const props = {
17
- content: ["test content"],
18
- meta: { key: "value" },
19
- error: new Error("test error"),
20
- priority: OutputMessage.PRIORITY.HIGH
21
- }
22
- const msg = new OutputMessage(props)
23
- assert.deepEqual(msg.content, ["test content"])
24
- assert.deepEqual(msg.meta, { key: "value" })
25
- assert.ok(msg.error instanceof Error)
26
- assert.equal(msg.priority, OutputMessage.PRIORITY.HIGH)
27
- })
28
-
29
- it("should set type based on error presence", () => {
30
- const errorMsg = new OutputMessage({ error: "error occurred" })
31
- const infoMsg = new OutputMessage({ content: ["info"] })
32
- assert.equal(errorMsg.type, "error")
33
- assert.equal(infoMsg.type, "info")
34
- })
35
-
36
- it("should handle string content", () => {
37
- const msg = new OutputMessage({ content: "single string" })
38
- assert.deepEqual(msg.content, ["single string"])
39
- })
40
-
41
- it("should combine messages", () => {
42
- const msg1 = new OutputMessage({ content: ["part1"], priority: 1 })
43
- const msg2 = new OutputMessage({ content: ["part2"], priority: 2 })
44
- const combined = msg1.combine(msg2)
45
- assert.deepEqual(combined.content, ["part1", "part2"])
46
- assert.equal(combined.priority, 2)
47
- })
48
-
49
- it("should convert to JSON", () => {
50
- const msg = new OutputMessage({
51
- content: ["test"],
52
- meta: { test: true },
53
- error: new Error("test error")
54
- })
55
- const json = msg.toJSON()
56
- assert.ok(json.body)
57
- assert.ok(json.meta)
58
- assert.ok(json.error)
59
- assert.ok(json.time)
60
- })
61
- })
@@ -1,35 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import OutputAdapter from "./OutputAdapter.js"
4
-
5
- describe("OutputAdapter", () => {
6
- it("should create instance", () => {
7
- const adapter = new OutputAdapter()
8
- assert.ok(adapter instanceof OutputAdapter)
9
- })
10
-
11
- it("should throw error on render method", () => {
12
- const adapter = new OutputAdapter()
13
- assert.throws(() => adapter.render(), {
14
- message: "render() must be implemented by subclass"
15
- })
16
- })
17
-
18
- it("should call render on progress", () => {
19
- const adapter = new OutputAdapter()
20
- let rendered = false
21
-
22
- // Override render for test
23
- adapter.render = () => {
24
- rendered = true
25
- }
26
-
27
- adapter.progress(0.5)
28
- assert.ok(rendered)
29
- })
30
-
31
- it("should stop without error", () => {
32
- const adapter = new OutputAdapter()
33
- assert.doesNotThrow(() => adapter.stop())
34
- })
35
- })
@@ -1,78 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import UIStream from "./Stream.js"
4
- import StreamEntry from "./StreamEntry.js"
5
-
6
- describe("UIStream", () => {
7
- it("should create processor generator", async () => {
8
- const controller = new AbortController()
9
- const processorFn = () => Promise.resolve("test result")
10
- const generator = UIStream.createProcessor(controller.signal, processorFn)
11
-
12
- assert.equal(typeof generator, "function")
13
-
14
- for await (const item of generator()) {
15
- assert.equal(item, "test result")
16
- break
17
- }
18
- })
19
-
20
- it("should handle aborted signal", async () => {
21
- const controller = new AbortController()
22
- controller.abort()
23
-
24
- const processorFn = () => Promise.resolve(StreamEntry.from({ value: "Hello", done: true }))
25
- const generator = UIStream.createProcessor(controller.signal, processorFn)
26
-
27
- for await (const item of generator()) {
28
- assert.deepEqual(item, StreamEntry.from({ done: true, value: "Hello" }))
29
- break
30
- }
31
- })
32
-
33
- it("should process stream with callbacks", async () => {
34
- const controller = new AbortController()
35
- let progressCalled = false
36
- let completeCalled = false
37
-
38
- const generatorFn = async function* () {
39
- yield StreamEntry.from({ progress: 0.5 })
40
- yield StreamEntry.from({ done: true })
41
- }
42
-
43
- const onProgress = () => { progressCalled = true }
44
- const onComplete = () => { completeCalled = true }
45
-
46
- await UIStream.process(
47
- controller.signal,
48
- generatorFn,
49
- onProgress,
50
- null,
51
- onComplete
52
- )
53
-
54
- assert.ok(progressCalled)
55
- assert.ok(completeCalled)
56
- })
57
-
58
- it("should handle errors in stream processing", async () => {
59
- const controller = new AbortController()
60
- let errorCalled = false
61
-
62
- const generatorFn = async function* () {
63
- yield { error: "test error" }
64
- }
65
-
66
- const onError = () => { errorCalled = true }
67
-
68
- await UIStream.process(
69
- controller.signal,
70
- generatorFn,
71
- null,
72
- onError,
73
- null
74
- )
75
-
76
- assert.ok(errorCalled)
77
- })
78
- })
package/src/index.test.js DELETED
@@ -1,14 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
- import { App } from "@nan0web/ui"
4
-
5
- describe("App", () => {
6
- it("should be defined", () => {
7
- assert.ok(App)
8
- assert.ok(App.Core)
9
- assert.ok(App.User)
10
- assert.ok(App.Command)
11
- assert.ok(App.Scenario)
12
- assert.ok(App.UI)
13
- })
14
- })
@@ -1,15 +0,0 @@
1
- import View from "../../src/View/View.js"
2
- import Welcome from "../../src/Component/Welcome/index.js"
3
- import Process from "../../src/Component/Process/index.js"
4
- import AppRenderOptions from "./RenderOptions.js"
5
-
6
- class AppView extends View {
7
- static RenderOptions = AppRenderOptions
8
- constructor(props = {}) {
9
- super(props)
10
- this.register(Welcome)
11
- this.register(Process)
12
- }
13
- }
14
-
15
- export default AppView
@@ -1,22 +0,0 @@
1
- import { describe, it, expect } from "vitest"
2
- import AppView from "./AppView.js"
3
- import Frame from "../../src/Frame/Frame.js"
4
-
5
- describe("AppView", () => {
6
- it("should create an instance", () => {
7
- const view = new AppView()
8
- expect(view).toBeInstanceOf(AppView)
9
- })
10
- it("should render a welcome component", () => {
11
- const view = new AppView()
12
- const welcome = view.render("Welcome")({ user: { name: "NaN•" } })
13
- expect(welcome).toBeInstanceOf(Frame)
14
- expect(view.stdout.stream).toEqual([
15
- [
16
- "Welcome NaN•!",
17
- "What can we do today great?",
18
- "",
19
- ].join("\n")
20
- ])
21
- })
22
- })