@uistate/examples 1.0.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.
- package/README.md +40 -0
- package/cssState/.gitkeep +0 -0
- package/eventState/001-counter/README.md +44 -0
- package/eventState/001-counter/index.html +33 -0
- package/eventState/002-counter-improved/README.md +44 -0
- package/eventState/002-counter-improved/index.html +47 -0
- package/eventState/003-input-reactive/README.md +44 -0
- package/eventState/003-input-reactive/index.html +33 -0
- package/eventState/004-computed-state/README.md +45 -0
- package/eventState/004-computed-state/index.html +65 -0
- package/eventState/005-conditional-rendering/README.md +42 -0
- package/eventState/005-conditional-rendering/index.html +39 -0
- package/eventState/006-list-rendering/README.md +49 -0
- package/eventState/006-list-rendering/index.html +63 -0
- package/eventState/007-form-validation/README.md +52 -0
- package/eventState/007-form-validation/index.html +102 -0
- package/eventState/008-undo-redo/README.md +70 -0
- package/eventState/008-undo-redo/index.html +108 -0
- package/eventState/009-localStorage-side-effects/README.md +72 -0
- package/eventState/009-localStorage-side-effects/index.html +57 -0
- package/eventState/010-decoupled-components/README.md +74 -0
- package/eventState/010-decoupled-components/index.html +93 -0
- package/eventState/011-async-patterns/README.md +98 -0
- package/eventState/011-async-patterns/index.html +132 -0
- package/eventState/028-counter-improved-eventTest/LICENSE +55 -0
- package/eventState/028-counter-improved-eventTest/README.md +131 -0
- package/eventState/028-counter-improved-eventTest/app/store.js +9 -0
- package/eventState/028-counter-improved-eventTest/index.html +49 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/router.js +271 -0
- package/eventState/028-counter-improved-eventTest/store.d.ts +8 -0
- package/eventState/028-counter-improved-eventTest/style.css +170 -0
- package/eventState/028-counter-improved-eventTest/tests/README.md +208 -0
- package/eventState/028-counter-improved-eventTest/tests/counter.test.js +116 -0
- package/eventState/028-counter-improved-eventTest/tests/eventTest.js +176 -0
- package/eventState/028-counter-improved-eventTest/tests/generateTypes.js +168 -0
- package/eventState/028-counter-improved-eventTest/tests/run.js +20 -0
- package/eventState/030-todo-app-with-eventTest/LICENSE +55 -0
- package/eventState/030-todo-app-with-eventTest/README.md +121 -0
- package/eventState/030-todo-app-with-eventTest/app/router.js +25 -0
- package/eventState/030-todo-app-with-eventTest/app/store.js +16 -0
- package/eventState/030-todo-app-with-eventTest/app/views/home.js +11 -0
- package/eventState/030-todo-app-with-eventTest/app/views/todoDemo.js +88 -0
- package/eventState/030-todo-app-with-eventTest/index.html +65 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/router.js +271 -0
- package/eventState/030-todo-app-with-eventTest/store.d.ts +18 -0
- package/eventState/030-todo-app-with-eventTest/style.css +170 -0
- package/eventState/030-todo-app-with-eventTest/tests/README.md +208 -0
- package/eventState/030-todo-app-with-eventTest/tests/eventTest.js +176 -0
- package/eventState/030-todo-app-with-eventTest/tests/generateTypes.js +189 -0
- package/eventState/030-todo-app-with-eventTest/tests/run.js +20 -0
- package/eventState/030-todo-app-with-eventTest/tests/todos.test.js +167 -0
- package/eventState/031-todo-app-with-eventTest/LICENSE +55 -0
- package/eventState/031-todo-app-with-eventTest/README.md +54 -0
- package/eventState/031-todo-app-with-eventTest/TUTORIAL.md +390 -0
- package/eventState/031-todo-app-with-eventTest/WHY_EVENTSTATE.md +777 -0
- package/eventState/031-todo-app-with-eventTest/app/bridges.js +113 -0
- package/eventState/031-todo-app-with-eventTest/app/router.js +26 -0
- package/eventState/031-todo-app-with-eventTest/app/store.js +15 -0
- package/eventState/031-todo-app-with-eventTest/app/views/home.js +46 -0
- package/eventState/031-todo-app-with-eventTest/app/views/todoDemo.js +69 -0
- package/eventState/031-todo-app-with-eventTest/devtools/dock.js +41 -0
- package/eventState/031-todo-app-with-eventTest/devtools/stateTracker.dock.js +10 -0
- package/eventState/031-todo-app-with-eventTest/devtools/stateTracker.js +246 -0
- package/eventState/031-todo-app-with-eventTest/devtools/telemetry.js +104 -0
- package/eventState/031-todo-app-with-eventTest/devtools/typeGenerator.js +339 -0
- package/eventState/031-todo-app-with-eventTest/index.html +103 -0
- package/eventState/031-todo-app-with-eventTest/package-lock.json +2184 -0
- package/eventState/031-todo-app-with-eventTest/package.json +24 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/router.js +271 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/boundary.js +36 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/converge.js +63 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +210 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/hydrate.js +157 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/queryBinding.js +69 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/computed.js +78 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/meta.js +51 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +28 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/validators.js +55 -0
- package/eventState/031-todo-app-with-eventTest/store.d.ts +23 -0
- package/eventState/031-todo-app-with-eventTest/style.css +170 -0
- package/eventState/031-todo-app-with-eventTest/tests/README.md +208 -0
- package/eventState/031-todo-app-with-eventTest/tests/eventTest.js +176 -0
- package/eventState/031-todo-app-with-eventTest/tests/generateTypes.js +191 -0
- package/eventState/031-todo-app-with-eventTest/tests/run.js +20 -0
- package/eventState/031-todo-app-with-eventTest/tests/todos.test.js +192 -0
- package/eventState/032-todo-app-with-eventTest/LICENSE +55 -0
- package/eventState/032-todo-app-with-eventTest/README.md +54 -0
- package/eventState/032-todo-app-with-eventTest/TUTORIAL.md +390 -0
- package/eventState/032-todo-app-with-eventTest/WHY_EVENTSTATE.md +777 -0
- package/eventState/032-todo-app-with-eventTest/app/actions/index.js +153 -0
- package/eventState/032-todo-app-with-eventTest/app/bridges.js +113 -0
- package/eventState/032-todo-app-with-eventTest/app/router.js +26 -0
- package/eventState/032-todo-app-with-eventTest/app/store.js +15 -0
- package/eventState/032-todo-app-with-eventTest/app/views/home.js +46 -0
- package/eventState/032-todo-app-with-eventTest/app/views/todoDemo.js +69 -0
- package/eventState/032-todo-app-with-eventTest/devtools/dock.js +41 -0
- package/eventState/032-todo-app-with-eventTest/devtools/stateTracker.dock.js +10 -0
- package/eventState/032-todo-app-with-eventTest/devtools/stateTracker.js +246 -0
- package/eventState/032-todo-app-with-eventTest/devtools/telemetry.js +104 -0
- package/eventState/032-todo-app-with-eventTest/devtools/typeGenerator.js +339 -0
- package/eventState/032-todo-app-with-eventTest/index.html +87 -0
- package/eventState/032-todo-app-with-eventTest/package-lock.json +2184 -0
- package/eventState/032-todo-app-with-eventTest/package.json +24 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/router.js +271 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/boundary.js +36 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/converge.js +63 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +210 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/hydrate.js +157 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/queryBinding.js +69 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/computed.js +78 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/meta.js +51 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +28 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/validators.js +55 -0
- package/eventState/032-todo-app-with-eventTest/store.d.ts +23 -0
- package/eventState/032-todo-app-with-eventTest/style.css +170 -0
- package/eventState/032-todo-app-with-eventTest/tests/README.md +208 -0
- package/eventState/032-todo-app-with-eventTest/tests/eventTest.js +176 -0
- package/eventState/032-todo-app-with-eventTest/tests/generateTypes.js +191 -0
- package/eventState/032-todo-app-with-eventTest/tests/run.js +20 -0
- package/eventState/032-todo-app-with-eventTest/tests/todos.test.js +192 -0
- package/package.json +27 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* generateTypes.js - Generate TypeScript definitions from test assertions
|
|
4
|
+
*
|
|
5
|
+
* Usage: node tests/generateTypes.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createEventTest } from './eventTest.js';
|
|
9
|
+
import todosTests from './todos.test.js';
|
|
10
|
+
import { writeFileSync } from 'fs';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { dirname, join } from 'path';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
|
|
17
|
+
// Collect all type assertions from tests
|
|
18
|
+
const allAssertions = [];
|
|
19
|
+
|
|
20
|
+
console.log('🔍 Extracting types from tests...\n');
|
|
21
|
+
|
|
22
|
+
for (const [name, testFn] of Object.entries(todosTests)) {
|
|
23
|
+
try {
|
|
24
|
+
// Run test to collect assertions
|
|
25
|
+
testFn();
|
|
26
|
+
console.log(`✓ ${name}`);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// Test might fail, but we still want type assertions
|
|
29
|
+
console.log(`⚠ ${name} (failed but types extracted)`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Simple type generator (minimal version for Node.js)
|
|
34
|
+
class SimpleTypeGenerator {
|
|
35
|
+
constructor() {
|
|
36
|
+
this.pathTypes = new Map();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
parseAssertions(assertions) {
|
|
40
|
+
for (const assertion of assertions) {
|
|
41
|
+
const { path, type, elementShape, shape } = assertion;
|
|
42
|
+
|
|
43
|
+
if (type === 'array' && elementShape) {
|
|
44
|
+
this.pathTypes.set(path, {
|
|
45
|
+
type: 'array',
|
|
46
|
+
element: elementShape
|
|
47
|
+
});
|
|
48
|
+
} else if (type === 'object' && shape) {
|
|
49
|
+
this.pathTypes.set(path, {
|
|
50
|
+
type: 'object',
|
|
51
|
+
shape: shape
|
|
52
|
+
});
|
|
53
|
+
} else if (type) {
|
|
54
|
+
this.pathTypes.set(path, { type });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
buildTree() {
|
|
60
|
+
const tree = {};
|
|
61
|
+
|
|
62
|
+
for (const [path, typeInfo] of this.pathTypes) {
|
|
63
|
+
const parts = path.split('.');
|
|
64
|
+
let current = tree;
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < parts.length; i++) {
|
|
67
|
+
const part = parts[i];
|
|
68
|
+
|
|
69
|
+
if (i === parts.length - 1) {
|
|
70
|
+
current[part] = { __typeInfo: typeInfo };
|
|
71
|
+
} else {
|
|
72
|
+
if (!current[part]) {
|
|
73
|
+
current[part] = {};
|
|
74
|
+
}
|
|
75
|
+
current = current[part];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return tree;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
formatType(typeInfo) {
|
|
84
|
+
if (typeof typeInfo === 'string') return typeInfo;
|
|
85
|
+
|
|
86
|
+
if (typeInfo.type === 'array' && typeInfo.element) {
|
|
87
|
+
const elementType = this.formatShape(typeInfo.element);
|
|
88
|
+
return `Array<${elementType}>`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeInfo.type === 'object' && typeInfo.shape) {
|
|
92
|
+
return this.formatShape(typeInfo.shape);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return typeInfo.type || 'unknown';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
formatShape(shape) {
|
|
99
|
+
if (typeof shape === 'string') return shape;
|
|
100
|
+
|
|
101
|
+
const props = Object.entries(shape)
|
|
102
|
+
.map(([k, v]) => {
|
|
103
|
+
const type = typeof v === 'string' ? v : this.formatShape(v);
|
|
104
|
+
return `${k}: ${type}`;
|
|
105
|
+
})
|
|
106
|
+
.join('; ');
|
|
107
|
+
|
|
108
|
+
return `{ ${props} }`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
generateInterface(obj, indent = 0) {
|
|
112
|
+
const lines = [];
|
|
113
|
+
const spaces = ' '.repeat(indent);
|
|
114
|
+
|
|
115
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
116
|
+
if (value.__typeInfo) {
|
|
117
|
+
const type = this.formatType(value.__typeInfo);
|
|
118
|
+
lines.push(`${spaces}${key}: ${type};`);
|
|
119
|
+
} else {
|
|
120
|
+
lines.push(`${spaces}${key}: {`);
|
|
121
|
+
lines.push(this.generateInterface(value, indent + 1));
|
|
122
|
+
lines.push(`${spaces}};`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return lines.join('\n');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
generateDTS() {
|
|
130
|
+
const lines = [
|
|
131
|
+
'// Auto-generated from test assertions',
|
|
132
|
+
'// DO NOT EDIT - regenerate by running: node tests/generateTypes.js',
|
|
133
|
+
'',
|
|
134
|
+
'export interface StoreState {',
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const tree = this.buildTree();
|
|
138
|
+
lines.push(this.generateInterface(tree, 1));
|
|
139
|
+
lines.push('}');
|
|
140
|
+
lines.push('');
|
|
141
|
+
lines.push('export default StoreState;');
|
|
142
|
+
lines.push('');
|
|
143
|
+
|
|
144
|
+
return lines.join('\n');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Note: We need to actually collect assertions from test execution
|
|
149
|
+
// For now, manually define expected types based on test assertions
|
|
150
|
+
const manualAssertions = [
|
|
151
|
+
{
|
|
152
|
+
path: 'domain.todos.items',
|
|
153
|
+
type: 'array',
|
|
154
|
+
elementShape: {
|
|
155
|
+
id: 'number',
|
|
156
|
+
text: 'string',
|
|
157
|
+
done: 'boolean'
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
path: 'ui.todos.filter',
|
|
162
|
+
type: 'string'
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
path: 'intent.todo.add',
|
|
166
|
+
type: 'object',
|
|
167
|
+
shape: {
|
|
168
|
+
text: 'string'
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
path: 'intent.todo.toggle',
|
|
173
|
+
type: 'object',
|
|
174
|
+
shape: {
|
|
175
|
+
id: 'number'
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
const generator = new SimpleTypeGenerator();
|
|
181
|
+
generator.parseAssertions(manualAssertions);
|
|
182
|
+
|
|
183
|
+
const dts = generator.generateDTS();
|
|
184
|
+
|
|
185
|
+
// Write to file
|
|
186
|
+
const outputPath = join(__dirname, '..', 'store.d.ts');
|
|
187
|
+
writeFileSync(outputPath, dts, 'utf-8');
|
|
188
|
+
|
|
189
|
+
console.log(`\n✅ Generated ${outputPath}`);
|
|
190
|
+
console.log('\n📝 Type definitions:\n');
|
|
191
|
+
console.log(dts);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* run.js - Test runner for Node.js
|
|
4
|
+
*
|
|
5
|
+
* Usage: node tests/run.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { runTests } from './eventTest.js';
|
|
9
|
+
import todosTests from './todos.test.js';
|
|
10
|
+
|
|
11
|
+
// Combine all test suites
|
|
12
|
+
const allTests = {
|
|
13
|
+
...todosTests
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Run tests
|
|
17
|
+
const results = runTests(allTests);
|
|
18
|
+
|
|
19
|
+
// Exit with appropriate code
|
|
20
|
+
process.exit(results.failed > 0 ? 1 : 0);
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* todos.test.js - Event-sequence tests for todo functionality
|
|
3
|
+
*
|
|
4
|
+
* These tests define the behavior AND types of the todo domain
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createEventTest, test, runTests } from './eventTest.js';
|
|
8
|
+
|
|
9
|
+
// Helper to set up todo bridges on a test store
|
|
10
|
+
function setupTodoBridges(store) {
|
|
11
|
+
let nextId = 1;
|
|
12
|
+
|
|
13
|
+
// Add todo
|
|
14
|
+
store.subscribe('intent.todo.add', (detail) => {
|
|
15
|
+
const { text } = detail;
|
|
16
|
+
const items = store.get('domain.todos.items') || [];
|
|
17
|
+
const todo = { id: nextId++, text: String(text || '').trim(), done: false };
|
|
18
|
+
if (!todo.text) return;
|
|
19
|
+
store.set('domain.todos.items', [...items, todo]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Toggle todo
|
|
23
|
+
store.subscribe('intent.todo.toggle', (detail) => {
|
|
24
|
+
const { id } = detail;
|
|
25
|
+
const items = store.get('domain.todos.items') || [];
|
|
26
|
+
const out = items.map(t => (String(t?.id) === String(id)) ? { ...t, done: !t.done } : t);
|
|
27
|
+
store.set('domain.todos.items', out);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Clear completed
|
|
31
|
+
store.subscribe('intent.todo.clearCompleted', () => {
|
|
32
|
+
const items = store.get('domain.todos.items') || [];
|
|
33
|
+
store.set('domain.todos.items', items.filter(t => !t.done));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// UI filter
|
|
37
|
+
store.subscribe('intent.ui.filter', (detail) => {
|
|
38
|
+
const { filter } = detail;
|
|
39
|
+
const f = (filter === 'active' || filter === 'completed') ? filter : 'all';
|
|
40
|
+
store.set('ui.todos.filter', f);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Test suite
|
|
45
|
+
const tests = {
|
|
46
|
+
'add todo creates correct structure': () => {
|
|
47
|
+
const t = createEventTest({
|
|
48
|
+
domain: { todos: { items: [] } }
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Set up bridges for this test
|
|
52
|
+
setupTodoBridges(t.store);
|
|
53
|
+
|
|
54
|
+
// Trigger intent
|
|
55
|
+
t.trigger('intent.todo.add', { text: 'Buy milk' });
|
|
56
|
+
|
|
57
|
+
// Assert array structure and element types
|
|
58
|
+
t.assertArrayOf('domain.todos.items', {
|
|
59
|
+
id: 'number',
|
|
60
|
+
text: 'string',
|
|
61
|
+
done: 'boolean'
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Assert array length
|
|
65
|
+
t.assertArrayLength('domain.todos.items', 1);
|
|
66
|
+
|
|
67
|
+
// Assert specific values
|
|
68
|
+
const items = t.store.get('domain.todos.items');
|
|
69
|
+
if (items[0].text !== 'Buy milk') {
|
|
70
|
+
throw new Error('Expected first todo text to be "Buy milk"');
|
|
71
|
+
}
|
|
72
|
+
if (items[0].done !== false) {
|
|
73
|
+
throw new Error('Expected first todo to not be done');
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
'toggle todo changes done state': () => {
|
|
78
|
+
const t = createEventTest({
|
|
79
|
+
domain: { todos: { items: [{ id: 1, text: 'Buy milk', done: false }] } }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Set up bridges for this test
|
|
83
|
+
setupTodoBridges(t.store);
|
|
84
|
+
|
|
85
|
+
// Trigger toggle
|
|
86
|
+
t.trigger('intent.todo.toggle', { id: 1 });
|
|
87
|
+
|
|
88
|
+
// Assert structure
|
|
89
|
+
t.assertArrayOf('domain.todos.items', {
|
|
90
|
+
id: 'number',
|
|
91
|
+
text: 'string',
|
|
92
|
+
done: 'boolean'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Assert done is now true
|
|
96
|
+
const items = t.store.get('domain.todos.items');
|
|
97
|
+
if (items[0].done !== true) {
|
|
98
|
+
throw new Error('Expected todo to be done');
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
'clear completed removes done todos': () => {
|
|
103
|
+
const t = createEventTest({
|
|
104
|
+
domain: {
|
|
105
|
+
todos: {
|
|
106
|
+
items: [
|
|
107
|
+
{ id: 1, text: 'Buy milk', done: true },
|
|
108
|
+
{ id: 2, text: 'Walk dog', done: false }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Set up bridges for this test
|
|
115
|
+
setupTodoBridges(t.store);
|
|
116
|
+
|
|
117
|
+
// Trigger clear
|
|
118
|
+
t.trigger('intent.todo.clearCompleted');
|
|
119
|
+
|
|
120
|
+
// Assert only active todo remains
|
|
121
|
+
t.assertArrayLength('domain.todos.items', 1);
|
|
122
|
+
|
|
123
|
+
const items = t.store.get('domain.todos.items');
|
|
124
|
+
if (items[0].text !== 'Walk dog') {
|
|
125
|
+
throw new Error('Expected only "Walk dog" to remain');
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
'filter intent updates filter state': () => {
|
|
130
|
+
const t = createEventTest({
|
|
131
|
+
ui: { todos: { filter: 'all' } }
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Set up bridges for this test
|
|
135
|
+
setupTodoBridges(t.store);
|
|
136
|
+
|
|
137
|
+
// Trigger filter change
|
|
138
|
+
t.trigger('intent.ui.filter', { filter: 'active' });
|
|
139
|
+
|
|
140
|
+
// Assert type
|
|
141
|
+
t.assertType('ui.todos.filter', 'string');
|
|
142
|
+
|
|
143
|
+
// Assert value
|
|
144
|
+
t.assertPath('ui.todos.filter', 'active');
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
'intent.todo.add has correct shape': () => {
|
|
148
|
+
const t = createEventTest({});
|
|
149
|
+
|
|
150
|
+
// Trigger intent
|
|
151
|
+
t.trigger('intent.todo.add', { text: 'Buy milk' });
|
|
152
|
+
|
|
153
|
+
// Assert intent shape (for type generation)
|
|
154
|
+
t.assertShape('intent.todo.add', {
|
|
155
|
+
text: 'string'
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
'intent.todo.toggle has correct shape': () => {
|
|
160
|
+
const t = createEventTest({});
|
|
161
|
+
|
|
162
|
+
// Trigger intent
|
|
163
|
+
t.trigger('intent.todo.toggle', { id: 1 });
|
|
164
|
+
|
|
165
|
+
// Assert intent shape
|
|
166
|
+
t.assertShape('intent.todo.toggle', {
|
|
167
|
+
id: 'number'
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
'empty todos array is valid': () => {
|
|
172
|
+
const t = createEventTest({
|
|
173
|
+
domain: { todos: { items: [] } }
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Assert empty array is still typed correctly
|
|
177
|
+
t.assertArrayOf('domain.todos.items', {
|
|
178
|
+
id: 'number',
|
|
179
|
+
text: 'string',
|
|
180
|
+
done: 'boolean'
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
t.assertArrayLength('domain.todos.items', 0);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Run tests if executed directly
|
|
188
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
189
|
+
runTests(tests);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default tests;
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uistate/examples",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Example applications and patterns for @uistate/core — eventState and cssState",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"eventState/",
|
|
8
|
+
"cssState/"
|
|
9
|
+
],
|
|
10
|
+
"keywords": [
|
|
11
|
+
"uistate",
|
|
12
|
+
"examples",
|
|
13
|
+
"state-management",
|
|
14
|
+
"event-driven",
|
|
15
|
+
"css-state",
|
|
16
|
+
"tutorials"
|
|
17
|
+
],
|
|
18
|
+
"author": "Ajdin Imsirovic",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/ImsirovicAjdin/uistate-examples"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@uistate/core": ">=5.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|