@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.
Files changed (57) hide show
  1. package/CHANGELOG.md +237 -0
  2. package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +19 -15
  3. package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +39 -40
  4. package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +22 -7
  5. package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +9 -4
  6. package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +146 -422
  7. package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +360 -0
  8. package/docs/academy/01-GetStarted/home.mdx +16 -24
  9. package/docs/academy/01-GetStarted/styles.module.css +31 -0
  10. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +0 -18
  11. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +10 -6
  12. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
  13. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +33 -16
  14. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +73 -0
  15. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +59 -4
  16. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
  17. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
  18. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +90 -228
  19. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +41 -1
  20. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +342 -67
  21. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +5 -3
  22. package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
  23. package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +162 -73
  24. package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
  25. package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +10 -9
  26. package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +3 -4
  27. package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +1 -1
  28. package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
  29. package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +13 -49
  30. package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +3 -0
  31. package/docs/academy/05-Architecture/images/PowerhouseArchitecture.png +0 -0
  32. package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +85 -30
  33. package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +382 -0
  34. package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +124 -0
  35. package/docs/academy/07-Cookbook.md +252 -4
  36. package/docs/academy/08-Glossary.md +20 -18
  37. package/docs/academy/09-AIResources +131 -0
  38. package/docusaurus.config.ts +4 -0
  39. package/package.json +1 -1
  40. package/sidebars.ts +3 -45
  41. package/src/css/custom.css +23 -1
  42. package/docs/academy/03-ExampleUsecases/Chatroom/01-SetupBuilderEnvironment.md +0 -216
  43. package/docs/academy/06-ComponentLibrary/02-BuildingWithScalars.md +0 -54
  44. package/docs/academy/06-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +0 -72
  45. package/docs/academy/06-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
  46. package/docs/academy/06-ComponentLibrary/04-Complex-Components/01-sidebar.mdx +0 -36
  47. package/docs/academy/06-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +0 -61
  48. package/docs/academy/06-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +0 -61
  49. /package/docs/academy/02-MasteryTrack/05-Launch/{02-IntroductionToPackages.md → 01-IntroductionToPackages.md} +0 -0
  50. /package/docs/academy/02-MasteryTrack/05-Launch/{00-IntegrateInAFront-End → _00-IntegrateInAFront-End} +0 -0
  51. /package/docs/academy/02-MasteryTrack/05-Launch/{01-IntroducingFusion → _01-IntroducingFusion} +0 -0
  52. /package/docs/academy/02-MasteryTrack/05-Launch/{04-GraphQLNamespacing → _04-GraphQLNamespacing} +0 -0
  53. /package/docs/academy/02-MasteryTrack/05-Launch/{05-LaunchYourBackend.md → _05-LaunchYourBackend} +0 -0
  54. /package/docs/academy/02-MasteryTrack/05-Launch/{06-LaunchYourFrontend.md → _06-LaunchYourFrontend} +0 -0
  55. /package/docs/academy/04-APIReferences/{01-ReactHooks.md → 01-ReactHooks} +0 -0
  56. /package/docs/academy/04-APIReferences/{02-ReactorAPI.md → 02-ReactorAPI} +0 -0
  57. /package/docs/academy/04-APIReferences/{03-Configuration.md → 03-Configuration} +0 -0
@@ -2,267 +2,124 @@
2
2
 
3
3
  ## Ensuring Robustness and Reliability
4
4
 
5
- In the previous sections, we've meticulously defined our document model's state schema, specified its operations, used the generator to scaffold our codebase, and implemented the core reducer logic. Now, we reach a critical stage that underpins the reliability and correctness of our entire document model: **Implementing Document Model Tests**.
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 merely an afterthought; it's an integral part of the development lifecycle, especially in systems like Powerhouse where data integrity, predictable state transitions, and auditable histories are paramount. Well-crafted tests serve as a safety net, allowing you to refactor and extend your document model with confidence, ensuring that it behaves as expected under all conditions.
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 will provide a deep dive into testing Powerhouse document models, focusing on the testing of reducer logic, which forms the heart of your model's behavior.
9
+ This document provides a practical, hands-on tutorial for testing the `ToDoList` document model reducers you have just built.
10
10
 
11
- ## The Importance of Testing Document Model Reducers
11
+ ## Practical Implementation: Writing and Running the ToDoList Tests
12
12
 
13
- Document model reducers, as explored in "[Implement Document Reducers](05-ImplementDocumentReducers.md)", are pure functions responsible for calculating the next state of a document based on its current state and a given operation. Their purity and deterministic nature make them inherently testable. Testing reducers thoroughly ensures:
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
- 1. **Correctness of Business Logic**: Verifies that each operation transforms the document state according to the defined business rules.
16
- 2. **State Immutability**: Confirms that reducers do not mutate the original state, a cornerstone of Powerhouse's event sourcing architecture.
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
- ## Setting Up the Test Environment
18
+ ### 1. Implement the Reducer Tests
22
19
 
23
- Powerhouse projects, when initialized using `ph init` (as detailed in "[Standard Document Model Workflow](../01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md)"), typically come pre-configured with a testing framework like Jest. You'll find that test files often end with `.test.ts` (e.g., `your-model-name.test.ts`) and are co-located with the code they test, often in a `tests/` subdirectory within the `src/reducers/` folder.
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
- The Document Model Generator (`ph generate YourModelName.phdm.zip`) usually creates a skeleton test file for your reducers (e.g., `document-models/YourModelName/src/reducers/tests/your-model-name.test.ts`). This provides a great starting point.
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'; // For createDocument
61
- import { reducer } from '../../gen/reducer'; // The main reducer
62
- import * as creators from '../../gen/creators'; // Action creators
63
- import { ToDoListDocument, ToDoListState, ToDoItem } from '../../gen/types'; // Your model's types
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
- // Test suite for UPDATE_TODO_ITEM operation
113
- describe('updateTodoItemOperation', () => {
114
- let docWithItem: ToDoListDocument;
115
- const initialItemId = 'item-to-update';
30
+ describe('Todolist Operations', () => {
31
+ let document: ToDoListDocument;
116
32
 
117
33
  beforeEach(() => {
118
- // Setup: add an item that can be updated in subsequent tests
119
- const addAction = creators.addTodoItem({ id: initialItemId, text: 'Original Text' });
120
- docWithItem = reducer(initialDocument, addAction);
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 update the text of an existing item', () => {
124
- const updateInput = { id: initialItemId, text: 'Updated Text' };
125
- const action = creators.updateTodoItem(updateInput);
126
-
127
- const updatedDocument = reducer(docWithItem, action);
128
- const item = updatedDocument.state.global.items.find(i => i.id === initialItemId);
129
-
130
- expect(item).toBeDefined();
131
- expect(item?.text).toBe('Updated Text');
132
- // The 'checked' status should remain unchanged if not provided
133
- expect(item?.checked).toBe(false);
134
- // Verify immutability for the updated item and the items array
135
- expect(updatedDocument.state.global.items).not.toBe(docWithItem.state.global.items);
136
- expect(item).not.toBe(docWithItem.state.global.items.find(i => i.id === initialItemId));
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 update the checked status of an existing item', () => {
140
- const updateInput = { id: initialItemId, checked: true };
141
- const action = creators.updateTodoItem(updateInput);
142
-
143
- const updatedDocument = reducer(docWithItem, action);
144
- const item = updatedDocument.state.global.items.find(i => i.id === initialItemId);
145
-
146
- expect(item?.checked).toBe(true);
147
- // The 'text' should remain unchanged
148
- expect(item?.text).toBe('Original Text');
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 update both text and checked status', () => {
152
- const updateInput = { id: initialItemId, text: 'New Text Again', checked: true };
153
- const action = creators.updateTodoItem(updateInput);
81
+ it('should handle deleteTodoItem operation', () => {
82
+ const addInput = { id: '1', text: 'Buy milk' };
83
+ const deleteInput = { id: '1' };
154
84
 
155
- const updatedDocument = reducer(docWithItem, action);
156
- const item = updatedDocument.state.global.items.find(i => i.id === initialItemId);
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
- it('should throw an error or not change state if item to update is not found', () => {
163
- const updateInput = { id: 'non-existent-id', text: 'Wont matter' };
164
- const action = creators.updateTodoItem(updateInput);
165
-
166
- // Depending on your reducer's error handling for "item not found":
167
- // Option 1: Reducer throws an error (as in 01-GetStarted/03-ImplementOperationReducers.md example)
168
- expect(() => reducer(docWithItem, action)).toThrowError('Item with id non-existent-id not found');
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
- ### Key Assertions to Make:
99
+ ### 2. Run the Tests
232
100
 
233
- 1. **State Changes**:
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
- 2. **Immutability**:
239
- * **Crucial**: Always assert that your reducers return *new* state objects/arrays when changes occur, rather than mutating the original ones.
240
- * For objects: `expect(newState).not.toBe(oldState);`
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
- 3. **Operation History (if applicable at this testing layer)**:
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
- 4. **Edge Cases and Error Handling**:
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
- * **Isolate Tests**: Each `it` block should ideally test one specific aspect or scenario. `beforeEach` can help reset state for this.
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 (call the reducer).
261
- * **Assert**: Check if the outcome is as expected.
262
- * **Test Pure Logic**: Focus on testing the input/output behavior of your reducers. Avoid testing implementation details if possible.
263
- * **Cover All Operations**: Ensure every defined document operation has corresponding tests.
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 principles and practices outlined in this document, and by drawing on the generated code and examples provided by Powerhouse, 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.
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.
@@ -1 +1,41 @@
1
- # Example: ToDoList Repo
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
+