@everystate/test 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.
package/eventTest.js ADDED
@@ -0,0 +1,178 @@
1
+ /**
2
+ * @everystate/test: Event-Sequence Testing for EveryState
3
+ *
4
+ * Copyright (c) 2026 Ajdin Imsirovic. MIT License.
5
+ *
6
+ * Provides TDD-style testing with type extraction capabilities.
7
+ */
8
+
9
+ import { createEveryState } from '@everystate/core';
10
+
11
+ export function createEventTest(initialState = {}) {
12
+ const store = createEveryState(initialState);
13
+ const eventLog = [];
14
+ const typeAssertions = [];
15
+
16
+ // Spy on all events
17
+ store.subscribe('*', (detail) => {
18
+ const { path, value } = detail;
19
+ eventLog.push({ timestamp: Date.now(), path, value });
20
+ });
21
+
22
+ const api = {
23
+ store,
24
+
25
+ // Trigger a state change
26
+ trigger(path, value) {
27
+ store.set(path, value);
28
+ return this;
29
+ },
30
+
31
+ // Assert exact value
32
+ assertPath(path, expected) {
33
+ const actual = store.get(path);
34
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
35
+ throw new Error(`Expected ${path} to be ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
36
+ }
37
+ return this;
38
+ },
39
+
40
+ // Assert type (for type generation)
41
+ assertType(path, expectedType) {
42
+ const actual = store.get(path);
43
+ const actualType = typeof actual;
44
+
45
+ if (actualType !== expectedType) {
46
+ throw new Error(`Expected ${path} to be type ${expectedType}, got ${actualType}`);
47
+ }
48
+
49
+ // Store for type generation
50
+ typeAssertions.push({ path, type: expectedType });
51
+ return this;
52
+ },
53
+
54
+ // Assert array with element shape (for type generation)
55
+ assertArrayOf(path, elementShape) {
56
+ const actual = store.get(path);
57
+
58
+ if (!Array.isArray(actual)) {
59
+ throw new Error(`Expected ${path} to be an array, got ${typeof actual}`);
60
+ }
61
+
62
+ // Validate first element matches shape (if array not empty)
63
+ if (actual.length > 0) {
64
+ validateShape(actual[0], elementShape, path);
65
+ }
66
+
67
+ // Store for type generation
68
+ typeAssertions.push({ path, type: 'array', elementShape });
69
+ return this;
70
+ },
71
+
72
+ // Assert object shape (for type generation)
73
+ assertShape(path, objectShape) {
74
+ const actual = store.get(path);
75
+
76
+ if (typeof actual !== 'object' || actual === null || Array.isArray(actual)) {
77
+ throw new Error(`Expected ${path} to be an object, got ${typeof actual}`);
78
+ }
79
+
80
+ validateShape(actual, objectShape, path);
81
+
82
+ // Store for type generation
83
+ typeAssertions.push({ path, type: 'object', shape: objectShape });
84
+ return this;
85
+ },
86
+
87
+ // Assert array length
88
+ assertArrayLength(path, expectedLength) {
89
+ const actual = store.get(path);
90
+
91
+ if (!Array.isArray(actual)) {
92
+ throw new Error(`Expected ${path} to be an array`);
93
+ }
94
+
95
+ if (actual.length !== expectedLength) {
96
+ throw new Error(`Expected ${path} to have length ${expectedLength}, got ${actual.length}`);
97
+ }
98
+
99
+ return this;
100
+ },
101
+
102
+ // Assert event fired N times
103
+ assertEventFired(path, times) {
104
+ const count = eventLog.filter(e => e.path === path).length;
105
+ if (times !== undefined && count !== times) {
106
+ throw new Error(`Expected ${path} to fire ${times} times, fired ${count}`);
107
+ }
108
+ return this;
109
+ },
110
+
111
+ // Get event log
112
+ getEventLog() {
113
+ return [...eventLog];
114
+ },
115
+
116
+ // Get type assertions (for type generation)
117
+ getTypeAssertions() {
118
+ return [...typeAssertions];
119
+ }
120
+ };
121
+
122
+ return api;
123
+ }
124
+
125
+ // Helper to validate object shape
126
+ function validateShape(actual, shape, path) {
127
+ for (const [key, expectedType] of Object.entries(shape)) {
128
+ if (!(key in actual)) {
129
+ throw new Error(`Expected ${path} to have property ${key}`);
130
+ }
131
+
132
+ const actualValue = actual[key];
133
+
134
+ // Handle nested objects
135
+ if (typeof expectedType === 'object' && !Array.isArray(expectedType)) {
136
+ validateShape(actualValue, expectedType, `${path}.${key}`);
137
+ } else {
138
+ // Primitive type check
139
+ const actualType = typeof actualValue;
140
+ if (actualType !== expectedType) {
141
+ throw new Error(`Expected ${path}.${key} to be type ${expectedType}, got ${actualType}`);
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ // Simple test runner
148
+ export function test(name, fn) {
149
+ try {
150
+ fn();
151
+ console.log(`\u2713 ${name}`);
152
+ return true;
153
+ } catch (error) {
154
+ console.error(`\u2717 ${name}`);
155
+ console.error(` ${error.message}`);
156
+ return false;
157
+ }
158
+ }
159
+
160
+ // Run multiple tests
161
+ export function runTests(tests) {
162
+ console.log('\n\uD83E\uDDEA Running tests...\n');
163
+
164
+ let passed = 0;
165
+ let failed = 0;
166
+
167
+ for (const [name, fn] of Object.entries(tests)) {
168
+ if (test(name, fn)) {
169
+ passed++;
170
+ } else {
171
+ failed++;
172
+ }
173
+ }
174
+
175
+ console.log(`\n\uD83D\uDCCA Results: ${passed} passed, ${failed} failed\n`);
176
+
177
+ return { passed, failed };
178
+ }
package/index.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @everystate/test
3
+ *
4
+ * Event-sequence testing for EveryState.
5
+ * TDD-style testing with type extraction capabilities.
6
+ */
7
+
8
+ import type { EveryStateStore } from '@everystate/core';
9
+
10
+ export interface TestCase {
11
+ name: string;
12
+ fn: (store: EveryStateStore) => void | Promise<void>;
13
+ }
14
+
15
+ export interface TestResult {
16
+ name: string;
17
+ passed: boolean;
18
+ error?: Error;
19
+ duration: number;
20
+ }
21
+
22
+ export interface TestSuite {
23
+ /** Add a test case */
24
+ add(name: string, fn: (store: EveryStateStore) => void | Promise<void>): void;
25
+ /** Run all registered tests */
26
+ run(): Promise<TestResult[]>;
27
+ /** Get results summary */
28
+ results(): TestResult[];
29
+ }
30
+
31
+ /**
32
+ * Create a test suite for event-sequence testing.
33
+ */
34
+ export function createEventTest(store: EveryStateStore): TestSuite;
35
+
36
+ /**
37
+ * Define a single test case (shorthand).
38
+ */
39
+ export function test(name: string, fn: (store: EveryStateStore) => void | Promise<void>): TestCase;
40
+
41
+ /**
42
+ * Run an array of test cases against a store.
43
+ */
44
+ export function runTests(store: EveryStateStore, tests: TestCase[]): Promise<TestResult[]>;
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @everystate/test
3
3
  *
4
- * EveryState wrapper for @uistate/event-test
5
- * Re-exports all functionality from the underlying @uistate/event-test package
4
+ * Event-sequence testing for EveryState.
5
+ * TDD-style testing with type extraction capabilities.
6
6
  */
7
7
 
8
- export * from '@uistate/event-test';
8
+ export { createEventTest, test, runTests } from './eventTest.js';
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@everystate/test",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "EveryState Test: Testing library with TDD-style assertions and event-sequence tracking",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
+ "types": "index.d.ts",
7
8
  "keywords": [
8
9
  "everystate",
9
10
  "testing",
@@ -25,10 +26,9 @@
25
26
  },
26
27
  "files": [
27
28
  "index.js",
29
+ "eventTest.js",
30
+ "index.d.ts",
28
31
  "README.md",
29
32
  "LICENSE.md"
30
- ],
31
- "dependencies": {
32
- "@uistate/event-test": "^1.0.1"
33
- }
33
+ ]
34
34
  }