@powerhousedao/academy 2.5.0-dev.4 → 2.5.0-dev.40
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/CHANGELOG.md +237 -0
- package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +19 -15
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +39 -40
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +22 -7
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +9 -4
- package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +146 -422
- package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +360 -0
- package/docs/academy/01-GetStarted/home.mdx +16 -24
- package/docs/academy/01-GetStarted/styles.module.css +31 -0
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +0 -18
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +10 -6
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +33 -16
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +73 -0
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +59 -4
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +90 -228
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +41 -1
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +342 -67
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +5 -3
- package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
- package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +162 -73
- package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
- package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +10 -9
- package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +3 -4
- package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +1 -1
- package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
- package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +13 -49
- package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +3 -0
- package/docs/academy/05-Architecture/images/PowerhouseArchitecture.png +0 -0
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +85 -30
- package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +382 -0
- package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +124 -0
- package/docs/academy/07-Cookbook.md +252 -4
- package/docs/academy/08-Glossary.md +20 -18
- package/docs/academy/09-AIResources +131 -0
- package/docusaurus.config.ts +4 -0
- package/package.json +1 -1
- package/sidebars.ts +3 -45
- package/src/css/custom.css +23 -1
- package/docs/academy/03-ExampleUsecases/Chatroom/01-SetupBuilderEnvironment.md +0 -216
- package/docs/academy/06-ComponentLibrary/02-BuildingWithScalars.md +0 -54
- package/docs/academy/06-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +0 -72
- package/docs/academy/06-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
- package/docs/academy/06-ComponentLibrary/04-Complex-Components/01-sidebar.mdx +0 -36
- package/docs/academy/06-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +0 -61
- package/docs/academy/06-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +0 -61
- /package/docs/academy/02-MasteryTrack/05-Launch/{02-IntroductionToPackages.md → 01-IntroductionToPackages.md} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{00-IntegrateInAFront-End → _00-IntegrateInAFront-End} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{01-IntroducingFusion → _01-IntroducingFusion} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{04-GraphQLNamespacing → _04-GraphQLNamespacing} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{05-LaunchYourBackend.md → _05-LaunchYourBackend} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{06-LaunchYourFrontend.md → _06-LaunchYourFrontend} +0 -0
- /package/docs/academy/04-APIReferences/{01-ReactHooks.md → 01-ReactHooks} +0 -0
- /package/docs/academy/04-APIReferences/{02-ReactorAPI.md → 02-ReactorAPI} +0 -0
- /package/docs/academy/04-APIReferences/{03-Configuration.md → 03-Configuration} +0 -0
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md
CHANGED
|
@@ -2,267 +2,124 @@
|
|
|
2
2
|
|
|
3
3
|
## Ensuring Robustness and Reliability
|
|
4
4
|
|
|
5
|
-
In the previous
|
|
5
|
+
In the previous chapter, we implemented the core reducer logic for our document model. Now, we reach a critical stage that underpins the reliability and correctness of our entire model: **Implementing Document Model Tests**.
|
|
6
6
|
|
|
7
|
-
Testing is not
|
|
7
|
+
Testing is not an afterthought; it's an integral part of the development lifecycle, especially in systems like Powerhouse where data integrity and predictable state transitions are paramount. Well-crafted tests serve as a safety net, allowing you to refactor and extend your document model with confidence.
|
|
8
8
|
|
|
9
|
-
This document
|
|
9
|
+
This document provides a practical, hands-on tutorial for testing the `ToDoList` document model reducers you have just built.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Practical Implementation: Writing and Running the ToDoList Tests
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
This tutorial assumes you have implemented the `ToDoList` reducers as described in the previous chapter and that the code generator has created a test file skeleton at `document-models/to-do-list/src/reducers/tests/to-do-list.test.ts`.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
3. **Integrity of Operation History**: While reducers focus on state, tests can also implicitly verify that operations are being correctly structured for the event log (as seen in example tests in `01-GetStarted/03-ImplementOperationReducers.md`).
|
|
18
|
-
4. **Regression Prevention**: Protects against accidental breakage of existing functionality when new features are added or existing code is refactored.
|
|
19
|
-
5. **Living Documentation**: Well-written tests can serve as examples of how operations are intended to be used and how they affect the document state.
|
|
15
|
+
<details>
|
|
16
|
+
<summary>Tutorial: Implementing and Running the `ToDoList` Reducer Tests</summary>
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
### 1. Implement the Reducer Tests
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
With the reducer logic in place, it's critical to test it. Navigate to the generated test file at `document-models/to-do-list/src/reducers/tests/to-do-list.test.ts` and replace its contents with the following test suite.
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
This suite tests each operation, verifying not only that the `items` array is correct, but also that our `stats` object is updated as expected and that the operation itself is recorded properly in the document's history.
|
|
26
23
|
|
|
27
|
-
Key tools and concepts you'll encounter in these test files include:
|
|
28
|
-
|
|
29
|
-
* **`describe(name, fn)`**: Creates a block that groups together several related tests.
|
|
30
|
-
* **`it(name, fn)`** or **`test(name, fn)`**: This is your actual test case.
|
|
31
|
-
* **`beforeEach(fn)`**: A function that runs before each test case in a `describe` block. Useful for setting up a common initial state for each test.
|
|
32
|
-
* **`expect(value)`**: Used with "matcher" functions to assert that a certain value meets expectations. Common matchers include `toBe()`, `toEqual()`, `toHaveLength()`, `toThrow()`, etc.
|
|
33
|
-
|
|
34
|
-
## Core Components for Testing Document Model Reducers
|
|
35
|
-
|
|
36
|
-
When testing your document model reducers, you'll primarily interact with artifacts generated by the Powerhouse Document Model Generator and the reducer logic you've written:
|
|
37
|
-
|
|
38
|
-
1. **Initial Document State (`utils.createDocument()`):**
|
|
39
|
-
* The `gen/utils.ts` file (or similar) often exports a `createDocument()` function. This utility, derived from your state schema (including default values), is crucial for creating a fresh, predictable starting state for your document model instance before each test.
|
|
40
|
-
* Example: `document = utils.createDocument();`
|
|
41
|
-
|
|
42
|
-
2. **Action Creators (`creators` or `operations`):**
|
|
43
|
-
* The `gen/operations.ts` or `gen/creators.ts` file exports action creator functions for each operation defined in your schema. Using these functions (e.g., `creators.addTodoItem(input)`) instead of manually constructing action objects is highly recommended because they ensure your actions are correctly typed and structured, reducing the chance of errors in your tests.
|
|
44
|
-
* Example: `const action = creators.addTodoItem({ id: '1', text: 'Test item' });`
|
|
45
|
-
|
|
46
|
-
3. **The Main Reducer (`reducer`):**
|
|
47
|
-
* The primary export from your reducer implementation file (e.g., `document-models/YourModelName/src/reducers/your-model-name.ts`) is the main reducer object or function that combines all your individual operation handlers. This is what you'll call with the current state and an action to get the new state.
|
|
48
|
-
* In some generated setups (like the `ToDoList` example from `01-GetStarted`), there might be a top-level `reducer` function exported from `gen/reducer.ts` that wraps your custom reducer logic and handles the overall document structure (state, operations history).
|
|
49
|
-
* Example: `const updatedDocument = reducer(document, action);`
|
|
50
|
-
|
|
51
|
-
4. **Generated Types (`types.ts`):**
|
|
52
|
-
* Using the TypeScript types from `gen/types.ts` (e.g., `ToDoListState`, `ToDoItem`, input types) in your test setup and assertions helps maintain type safety and clarity in your tests.
|
|
53
|
-
|
|
54
|
-
## Writing Effective Test Cases for Reducers
|
|
55
|
-
|
|
56
|
-
Let's draw inspiration from the `ToDoList` example tests found in `01-GetStarted/03-ImplementOperationReducers.md` and expand on the principles.
|
|
57
|
-
|
|
58
|
-
A typical test file structure:
|
|
59
24
|
```typescript
|
|
60
|
-
import utils from '../../gen/utils';
|
|
61
|
-
import { reducer } from '../../gen/reducer';
|
|
62
|
-
import * as creators from '../../gen/creators';
|
|
63
|
-
import { ToDoListDocument
|
|
64
|
-
|
|
65
|
-
describe('ToDoList Document Model Operations', () => {
|
|
66
|
-
let initialDocument: ToDoListDocument; // Or your specific document type
|
|
67
|
-
|
|
68
|
-
beforeEach(() => {
|
|
69
|
-
// Start with a fresh, empty document for each test
|
|
70
|
-
initialDocument = utils.createDocument();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Test suite for ADD_TODO_ITEM operation
|
|
74
|
-
describe('addTodoItemOperation', () => {
|
|
75
|
-
it('should add a new item to an empty list', () => {
|
|
76
|
-
const input = { id: 'task1', text: 'Buy groceries' };
|
|
77
|
-
const action = creators.addTodoItem(input);
|
|
78
|
-
|
|
79
|
-
const updatedDocument = reducer(initialDocument, action);
|
|
80
|
-
const state = updatedDocument.state.global; // Accessing the specific state slice
|
|
81
|
-
|
|
82
|
-
expect(state.items).toHaveLength(1);
|
|
83
|
-
expect(state.items[0]).toEqual({
|
|
84
|
-
id: 'task1',
|
|
85
|
-
text: 'Buy groceries',
|
|
86
|
-
checked: false, // Assuming default
|
|
87
|
-
});
|
|
88
|
-
// Verify immutability: the new items array should be a different instance
|
|
89
|
-
expect(state.items).not.toBe(initialDocument.state.global.items);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should add a new item to an existing list', () => {
|
|
93
|
-
// First, add an initial item
|
|
94
|
-
const firstItemInput = { id: 'task1', text: 'First task' };
|
|
95
|
-
let currentDocument = reducer(initialDocument, creators.addTodoItem(firstItemInput));
|
|
96
|
-
|
|
97
|
-
// Now, add the second item
|
|
98
|
-
const secondItemInput = { id: 'task2', text: 'Second task' };
|
|
99
|
-
const action = creators.addTodoItem(secondItemInput);
|
|
100
|
-
currentDocument = reducer(currentDocument, action);
|
|
101
|
-
|
|
102
|
-
const state = currentDocument.state.global;
|
|
103
|
-
expect(state.items).toHaveLength(2);
|
|
104
|
-
expect(state.items[1]).toEqual({
|
|
105
|
-
id: 'task2',
|
|
106
|
-
text: 'Second task',
|
|
107
|
-
checked: false,
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
});
|
|
25
|
+
import utils from '../../gen/utils.js';
|
|
26
|
+
import { reducer } from '../../gen/reducer.js';
|
|
27
|
+
import * as creators from '../../gen/creators.js';
|
|
28
|
+
import { ToDoListDocument } from '../../gen/types.js';
|
|
111
29
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
let docWithItem: ToDoListDocument;
|
|
115
|
-
const initialItemId = 'item-to-update';
|
|
30
|
+
describe('Todolist Operations', () => {
|
|
31
|
+
let document: ToDoListDocument;
|
|
116
32
|
|
|
117
33
|
beforeEach(() => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
34
|
+
// REMARKS: We start with a fresh, empty document for each test.
|
|
35
|
+
// The `createDocument` utility initializes the state with an empty 'items' array
|
|
36
|
+
// and a 'stats' object with all counts set to 0.
|
|
37
|
+
document = utils.createDocument();
|
|
121
38
|
});
|
|
122
39
|
|
|
123
|
-
it('should
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
40
|
+
it('should handle addTodoItem operation', () => {
|
|
41
|
+
const input = { id: '1', text: 'Buy milk' };
|
|
42
|
+
|
|
43
|
+
// REMARKS: We apply the 'addTodoItem' operation.
|
|
44
|
+
const updatedDocument = reducer(document, creators.addTodoItem(input));
|
|
45
|
+
|
|
46
|
+
// REMARKS: We verify the operation was recorded in the document's history.
|
|
47
|
+
// Powerhouse records every operation in an array.
|
|
48
|
+
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
49
|
+
expect(updatedDocument.operations.global[0].type).toBe('ADD_TODO_ITEM');
|
|
50
|
+
// REMARKS: We also check that the input data and index are recorded correctly.
|
|
51
|
+
expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
|
|
52
|
+
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
53
|
+
|
|
54
|
+
// REMARKS: Finally, we verify the state was updated according to our reducer logic.
|
|
55
|
+
expect(updatedDocument.state.global.items).toHaveLength(1);
|
|
56
|
+
expect(updatedDocument.state.global.stats.total).toBe(1);
|
|
57
|
+
expect(updatedDocument.state.global.stats.unchecked).toBe(1);
|
|
137
58
|
});
|
|
138
59
|
|
|
139
|
-
it('should
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
60
|
+
it('should handle updateTodoItem operation', () => {
|
|
61
|
+
// REMARKS: For an update, we first need to add an item.
|
|
62
|
+
const addInput = { id: '1', text: 'Buy milk' };
|
|
63
|
+
const updateInput = { id: '1', checked: true }; // We'll test checking the item.
|
|
64
|
+
|
|
65
|
+
// REMARKS: Operations are applied sequentially to build up document state.
|
|
66
|
+
const createdDocument = reducer(document, creators.addTodoItem(addInput));
|
|
67
|
+
const updatedDocument = reducer(createdDocument, creators.updateTodoItem(updateInput));
|
|
68
|
+
|
|
69
|
+
// REMARKS: Now we should have 2 operations in the history.
|
|
70
|
+
expect(updatedDocument.operations.global).toHaveLength(2);
|
|
71
|
+
expect(updatedDocument.operations.global[1].type).toBe('UPDATE_TODO_ITEM');
|
|
72
|
+
expect(updatedDocument.operations.global[1].input).toStrictEqual(updateInput);
|
|
73
|
+
|
|
74
|
+
// REMARKS: We check that the state reflects the update, including our stats.
|
|
75
|
+
expect(updatedDocument.state.global.items[0].checked).toBe(true);
|
|
76
|
+
expect(updatedDocument.state.global.stats.total).toBe(1);
|
|
77
|
+
expect(updatedDocument.state.global.stats.unchecked).toBe(0);
|
|
78
|
+
expect(updatedDocument.state.global.stats.checked).toBe(1);
|
|
149
79
|
});
|
|
150
80
|
|
|
151
|
-
it('should
|
|
152
|
-
|
|
153
|
-
|
|
81
|
+
it('should handle deleteTodoItem operation', () => {
|
|
82
|
+
const addInput = { id: '1', text: 'Buy milk' };
|
|
83
|
+
const deleteInput = { id: '1' };
|
|
154
84
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
expect(item?.text).toBe('New Text Again');
|
|
159
|
-
expect(item?.checked).toBe(true);
|
|
160
|
-
});
|
|
85
|
+
const createdDocument = reducer(document, creators.addTodoItem(addInput));
|
|
86
|
+
const updatedDocument = reducer(createdDocument, creators.deleteTodoItem(deleteInput));
|
|
161
87
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
// Option 2: Reducer returns original state (if it handles errors gracefully by not changing state)
|
|
171
|
-
// const updatedDocument = reducer(docWithItem, action);
|
|
172
|
-
// expect(updatedDocument.state.global).toEqual(docWithItem.state.global);
|
|
88
|
+
// REMARKS: After deletion, we still have 2 operations in history,
|
|
89
|
+
// but the items array is now empty and the stats are back to zero.
|
|
90
|
+
expect(updatedDocument.operations.global).toHaveLength(2);
|
|
91
|
+
expect(updatedDocument.operations.global[1].type).toBe('DELETE_TODO_ITEM');
|
|
92
|
+
expect(updatedDocument.state.global.items).toHaveLength(0);
|
|
93
|
+
expect(updatedDocument.state.global.stats.total).toBe(0);
|
|
94
|
+
expect(updatedDocument.state.global.stats.unchecked).toBe(0);
|
|
173
95
|
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Test suite for DELETE_TODO_ITEM operation
|
|
177
|
-
describe('deleteTodoItemOperation', () => {
|
|
178
|
-
let docWithItems: ToDoListDocument;
|
|
179
|
-
const item1Id = 'item1';
|
|
180
|
-
const item2Id = 'item2';
|
|
181
|
-
|
|
182
|
-
beforeEach(() => {
|
|
183
|
-
// Setup: add multiple items
|
|
184
|
-
const addAction1 = creators.addTodoItem({ id: item1Id, text: 'Item One' });
|
|
185
|
-
let tempDoc = reducer(initialDocument, addAction1);
|
|
186
|
-
const addAction2 = creators.addTodoItem({ id: item2Id, text: 'Item Two' });
|
|
187
|
-
docWithItems = reducer(tempDoc, addAction2);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should delete an existing item', () => {
|
|
191
|
-
const deleteInput = { id: item1Id };
|
|
192
|
-
const action = creators.deleteTodoItem(deleteInput);
|
|
193
|
-
|
|
194
|
-
const updatedDocument = reducer(docWithItems, action);
|
|
195
|
-
const state = updatedDocument.state.global;
|
|
196
|
-
|
|
197
|
-
expect(state.items).toHaveLength(1);
|
|
198
|
-
expect(state.items.find(item => item.id === item1Id)).toBeUndefined();
|
|
199
|
-
expect(state.items[0].id).toBe(item2Id);
|
|
200
|
-
// Verify immutability
|
|
201
|
-
expect(state.items).not.toBe(docWithItems.state.global.items);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('should not change state if item to delete is not found', () => {
|
|
205
|
-
const deleteInput = { id: 'non-existent-id' };
|
|
206
|
-
const action = creators.deleteTodoItem(deleteInput);
|
|
207
|
-
|
|
208
|
-
const updatedDocument = reducer(docWithItems, action);
|
|
209
|
-
expect(updatedDocument.state.global.items).toHaveLength(2);
|
|
210
|
-
expect(updatedDocument.state.global).toEqual(docWithItems.state.global); // Or check for array instance equality
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Testing Operation History (as shown in 01-GetStarted/03-ImplementOperationReducers.md)
|
|
215
|
-
it('should record operations in the document history', () => {
|
|
216
|
-
const addAction = creators.addTodoItem({ id: 'hist1', text: 'History Test' });
|
|
217
|
-
let doc = reducer(initialDocument, addAction);
|
|
218
|
-
|
|
219
|
-
expect(doc.operations.global).toHaveLength(1);
|
|
220
|
-
expect(doc.operations.global[0].type).toBe('ADD_TODO_ITEM'); // Ensure this matches actual operation type string
|
|
221
|
-
|
|
222
|
-
const updateAction = creators.updateTodoItem({ id: 'hist1', checked: true });
|
|
223
|
-
doc = reducer(doc, updateAction);
|
|
224
|
-
|
|
225
|
-
expect(doc.operations.global).toHaveLength(2);
|
|
226
|
-
expect(doc.operations.global[1].type).toBe('UPDATE_TODO_ITEM');
|
|
227
|
-
});
|
|
228
96
|
});
|
|
229
97
|
```
|
|
230
98
|
|
|
231
|
-
###
|
|
99
|
+
### 2. Run the Tests
|
|
232
100
|
|
|
233
|
-
|
|
234
|
-
* Verify that the relevant parts of the state are updated correctly (e.g., an item is added to an array, a field is changed).
|
|
235
|
-
* Use `toEqual()` for deep equality checks on objects and arrays.
|
|
236
|
-
* Check array lengths, specific property values, etc.
|
|
101
|
+
Now, run the tests from your project's root directory to verify your implementation.
|
|
237
102
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
* For arrays: `expect(newState.items).not.toBe(oldState.items);`
|
|
242
|
-
* For modified items within an array: `expect(updatedItem).not.toBe(originalItem);`
|
|
103
|
+
```bash
|
|
104
|
+
pnpm run test
|
|
105
|
+
```
|
|
243
106
|
|
|
244
|
-
|
|
245
|
-
* As seen in `01-GetStarted/03-ImplementOperationReducers.md`, the top-level reducer provided by Powerhouse might also update an `operations` log in the document. If your tests involve this encompassing reducer, you can assert that operations are correctly recorded.
|
|
246
|
-
* `expect(updatedDocument.operations.global).toHaveLength(expectedCount);`
|
|
247
|
-
* `expect(updatedDocument.operations.global[index].type).toBe('EXPECTED_OPERATION_TYPE');`
|
|
107
|
+
If all tests pass, you have successfully verified the core logic of your `ToDoList` document model. This ensures that the reducers you wrote behave exactly as expected.
|
|
248
108
|
|
|
249
|
-
|
|
250
|
-
* Test what happens when an operation receives invalid input (e.g., trying to update/delete an item that doesn't exist).
|
|
251
|
-
* If your reducer is designed to throw errors, use `expect(() => reducer(...)).toThrowError('Expected error message');`.
|
|
252
|
-
* If it handles errors by returning the original state or a specific error state, test for that outcome.
|
|
109
|
+
</details>
|
|
253
110
|
|
|
254
111
|
## Best Practices for Document Model Tests
|
|
255
112
|
|
|
256
|
-
|
|
113
|
+
While the tutorial provides a concrete example, keep these general best practices in mind when writing your tests:
|
|
114
|
+
|
|
115
|
+
* **Isolate Tests**: Each `it` block should ideally test one specific aspect or scenario. `beforeEach` is crucial for resetting state between tests.
|
|
257
116
|
* **Descriptive Names**: Name your `describe` and `it` blocks clearly so they explain what's being tested.
|
|
258
117
|
* **AAA Pattern (Arrange, Act, Assert)**:
|
|
259
|
-
* **Arrange**: Set up the initial state and any required test data.
|
|
260
|
-
* **Act**: Execute the operation
|
|
261
|
-
* **Assert**: Check if the outcome is as expected
|
|
262
|
-
* **Test
|
|
263
|
-
* **Cover
|
|
264
|
-
* **Cover All State Transitions**: For each operation, test different scenarios that lead to different state changes or outcomes.
|
|
265
|
-
* **Refactor Tests with Code**: As your reducer logic evolves, update your tests accordingly. Outdated tests are worse than no tests.
|
|
118
|
+
* **Arrange**: Set up the initial state and any required test data (e.g., using `utils.createDocument()` and defining `input` objects).
|
|
119
|
+
* **Act**: Execute the operation by calling the `reducer` with an action from a `creator`.
|
|
120
|
+
* **Assert**: Check if the outcome is as expected using `expect()`.
|
|
121
|
+
* **Test Immutability**: A key assertion is to ensure the state is not mutated directly. You can check that a new array or object was created: `expect(newState.items).not.toBe(oldState.items);`.
|
|
122
|
+
* **Cover Edge Cases**: Test what happens when an operation receives invalid input (e.g., trying to update an item that doesn't exist). Your test should confirm the reducer either throws an error or returns the state unchanged, depending on your implementation.
|
|
266
123
|
* **Run Tests Frequently**: Integrate testing into your development workflow. Run tests after making changes to ensure you haven't broken anything. The `pnpm run test` command is your friend.
|
|
267
124
|
|
|
268
125
|
## Conclusion: The Payoff of Diligent Testing
|
|
@@ -274,4 +131,9 @@ Implementing comprehensive tests for your document model reducers is an investme
|
|
|
274
131
|
* **Easier Debugging**: When tests fail, they pinpoint the exact operation and scenario that's problematic.
|
|
275
132
|
* **Better Collaboration**: Tests clarify the intended behavior of the document model for all team members.
|
|
276
133
|
|
|
277
|
-
By following the
|
|
134
|
+
By following the tutorial and applying these best practices, you can build a strong suite of tests that safeguard the integrity and functionality of your document models. This diligence is a hallmark of a "Mastery Track" developer, ensuring that the solutions you build are not just functional but also stable, maintainable, and trustworthy.
|
|
135
|
+
|
|
136
|
+
## Up Next
|
|
137
|
+
In the next chapter of the Mastery Track - Building User Experiences you will learn how to implement an editor for your document model so you can see a simple user interface for the **ToDoList** document model in action.
|
|
138
|
+
|
|
139
|
+
For a complete, working example, you can always refer to the [Example ToDoList Repository](/academy/MasteryTrack/DocumentModelCreation/ExampleToDoListRepository) which contains the full implementation of the concepts discussed in this Mastery Track.
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md
CHANGED
|
@@ -1 +1,41 @@
|
|
|
1
|
-
# Example:
|
|
1
|
+
# Example: ToDo Demo Package
|
|
2
|
+
|
|
3
|
+
:::info
|
|
4
|
+
|
|
5
|
+
The ToDo Demo package is maintained by the Powerhouse Team and serves as a reference for testing and introducing new features. It will be continuously updated alongside the accompanying documentation.
|
|
6
|
+
|
|
7
|
+
https://github.com/powerhouse-inc/todo-demo-package
|
|
8
|
+
:::
|
|
9
|
+
|
|
10
|
+
There are several ways to explore this package:
|
|
11
|
+
|
|
12
|
+
### Option 1: Rebuild the ToDo Demo Package
|
|
13
|
+
|
|
14
|
+
The ToDo Demo package and repository are your main reference points during the Mastery Track.
|
|
15
|
+
Follow the steps in the "Mastery Track – Document Model Creation" chapters to build along with the examples.
|
|
16
|
+
|
|
17
|
+
### Option 2: Clone and Run the Code Locally
|
|
18
|
+
|
|
19
|
+
The package includes:
|
|
20
|
+
- The Document Model
|
|
21
|
+
- Reducer Code
|
|
22
|
+
- Reducer Tests
|
|
23
|
+
- Editor Code
|
|
24
|
+
- Drive Explorer Code
|
|
25
|
+
|
|
26
|
+
You can clone the repository and run Connect Studio to see all the code in action:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ph connect
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Option 3: Install the ToDo Demo Package in a (Local) Host App
|
|
33
|
+
|
|
34
|
+
Alternatively, you can install this package in a Powerhouse project or in your deployed host apps:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ph install @powerhousedao/todo-demo-package
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|