@powerhousedao/academy 5.1.0-dev.9 → 5.1.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +43 -1108
  2. package/blog/BeyondCommunication-ABlueprintForDevelopment.md +1 -2
  3. package/blog/TheChallengeOfChange.md +0 -1
  4. package/docs/academy/00-EthereumArgentinaHackathon.md +207 -0
  5. package/docs/academy/01-GetStarted/00-ExploreDemoPackage.mdx +27 -24
  6. package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +9 -118
  7. package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +28 -110
  8. package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +145 -191
  9. package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +218 -0
  10. package/docs/academy/{02-MasteryTrack/01-BuilderEnvironment → 01-GetStarted}/05-VetraStudio.md +6 -48
  11. package/docs/academy/01-GetStarted/06-ReactorMCP.md +58 -0
  12. package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +1 -1
  13. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +2 -2
  14. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +44 -75
  15. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +22 -28
  16. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +31 -28
  17. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +206 -211
  18. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +62 -176
  19. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +0 -21
  20. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +319 -309
  21. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/00-DocumentToolbar.mdx +0 -4
  22. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/01-OperationHistory.md +0 -4
  23. package/docs/academy/02-MasteryTrack/05-Launch/04-ConfigureEnvironment.md +1 -1
  24. package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +35 -111
  25. package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +76 -255
  26. package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +160 -281
  27. package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +35 -188
  28. package/docs/academy/03-ExampleUsecases/Chatroom/06-LaunchALocalReactor.md +7 -95
  29. package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
  30. package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +2 -6
  31. package/docs/academy/04-APIReferences/01-ReactHooks.md +501 -291
  32. package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +39 -7
  33. package/docs/academy/05-Architecture/02-ReferencingMonorepoPackages +65 -0
  34. package/docs/academy/05-Architecture/04-MovingBeyondCRUD +61 -0
  35. package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +24 -72
  36. package/docs/academy/08-Glossary.md +0 -7
  37. package/docusaurus.config.ts +3 -28
  38. package/package.json +1 -1
  39. package/sidebars.ts +12 -49
  40. package/src/css/custom.css +18 -26
  41. package/docs/academy/01-GetStarted/04-WriteDocumentModelTests.md +0 -378
  42. package/docs/academy/01-GetStarted/05-BuildToDoListEditor.md +0 -560
  43. package/docs/academy/03-ExampleUsecases/TodoList/00-GetTheStarterCode.md +0 -24
  44. package/docs/academy/03-ExampleUsecases/TodoList/01-GenerateTodoListDocumentModel.md +0 -211
  45. package/docs/academy/03-ExampleUsecases/TodoList/02-ImplementTodoListDocumentModelReducerOperationHandlers.md +0 -171
  46. package/docs/academy/03-ExampleUsecases/TodoList/03-AddTestsForTodoListActions.md +0 -462
  47. package/docs/academy/03-ExampleUsecases/TodoList/04-GenerateTodoListDocumentEditor.md +0 -45
  48. package/docs/academy/03-ExampleUsecases/TodoList/05-ImplementTodoListDocumentEditorUIComponents.md +0 -422
  49. package/docs/academy/03-ExampleUsecases/TodoList/06-GenerateTodoDriveExplorer.md +0 -61
  50. package/docs/academy/03-ExampleUsecases/TodoList/07-AddSharedComponentForShowingTodoListStats.md +0 -384
  51. package/docs/academy/03-ExampleUsecases/TodoList/_category_.json +0 -8
  52. package/docs/academy/03-ExampleUsecases/VetraPackageLibrary/VetraPackageLibrary.md +0 -7
  53. package/docs/academy/03-ExampleUsecases/VetraPackageLibrary/_category_.json +0 -9
  54. package/docs/academy/04-APIReferences/06-VetraRemoteDrive.md +0 -160
  55. package/docs/academy/04-APIReferences/renown-sdk/00-Overview.md +0 -316
  56. package/docs/academy/04-APIReferences/renown-sdk/01-Authentication.md +0 -672
  57. package/docs/academy/04-APIReferences/renown-sdk/02-APIReference.md +0 -957
  58. package/docs/academy/04-APIReferences/renown-sdk/_category_.json +0 -8
  59. package/docs/academy/10-TodoListTutorial/02-ImplementTodoListDocumentModelReducerOperationHandlers.md +0 -171
  60. package/docs/academy/10-TodoListTutorial/03-AddTestsForTodoListActions.md +0 -462
  61. package/docs/academy/10-TodoListTutorial/05-ImplementTodoListDocumentEditorUIComponents.md +0 -422
  62. package/docs/academy/10-TodoListTutorial/07-AddSharedComponentForShowingTodoListStats.md +0 -370
  63. package/static/img/Vetra-logo-dark.svg +0 -11
  64. package/static/img/vetra-logo-light.svg +0 -11
  65. /package/docs/academy/{02-MasteryTrack/01-BuilderEnvironment → 01-GetStarted}/images/Modules.png +0 -0
  66. /package/docs/academy/{02-MasteryTrack/01-BuilderEnvironment → 01-GetStarted}/images/VetraStudioDrive.png +0 -0
  67. /package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/{02-RevisionHistoryTimeline/360/237/232/247" → 02-RevisionHistoryTimeline} +0 -0
  68. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /01-WhatIsADocumentModel" → 01-WhatIsADocumentModel} +0 -0
  69. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /02-DAOandDocumentsModelsQ+A" → 02-DAOandDocumentsModelsQ+A} +0 -0
  70. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /02-domain-modeling" → 02-domain-modeling} +0 -0
  71. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /03-BenefitsOfDocumentModels" → 03-BenefitsOfDocumentModels} +0 -0
  72. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /04-UtilitiesAndTips" → 04-UtilitiesAndTips} +0 -0
  73. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /05-best-practices" → 05-best-practices} +0 -0
  74. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /_category_.json" → _category_.json} +0 -0
  75. /package/docs/academy/05-Architecture/05-DocumentModelTheory/{360/237/232/247 /three-data-layers.png" → three-data-layers.png} +0 -0
@@ -1,268 +1,159 @@
1
- # Implement the document model
1
+ # Implement Operation Reducers
2
2
 
3
- :::tip Tutorial Repository
4
- 📦 **Reference Code**: [chatroom-demo](https://github.com/powerhouse-inc/chatroom-demo)
3
+ In this section, we will implement and test the operation reducers for the `ChatRoom` document model. In order to do this, you have to export the document model from the Connect application and import it into your powerhouse project directory.
5
4
 
6
- This tutorial covers two key implementations:
7
- 1. **Reducers**: Implementing the reducer logic for all ChatRoom operations
8
- 2. **Tests**: Writing comprehensive tests for the reducers
5
+ To export the document model, follow the steps in the `Define Chatroom Document Model` section.
9
6
 
10
- You can view the exact implementation in the repository's `document-models/chat-room/src/` directory.
11
- :::
7
+ ## Import Document Model and Generate Code
12
8
 
13
- <details>
14
- <summary>📖 How to use this tutorial</summary>
9
+ To import the document model into your powerhouse project, you can either:
15
10
 
16
- This tutorial covers implementing reducers and tests.
11
+ - Copy&Paste the file directly into the root of your powerhouse project.
12
+ - Or drag&drop the file into the powerhouse project directory in the VSCode editor as seen in the image below:
17
13
 
18
- ### Compare your reducer implementation
19
-
20
- After implementing your reducers:
21
-
22
- ```bash
23
- # Compare your reducers with the reference
24
- git diff tutorial/main -- document-models/chat-room/src/reducers/
25
-
26
- # View the reference reducer implementation
27
- git show tutorial/main:document-models/chat-room/src/reducers/messages.ts
28
- ```
29
-
30
- ### Compare your tests
31
-
32
- After writing tests:
33
-
34
- ```bash
35
- # Compare your tests with the reference
36
- git diff tutorial/main -- document-models/chat-room/src/tests/
37
-
38
- # View the reference test implementation
39
- git show tutorial/main:document-models/chat-room/src/tests/messages.test.ts
40
- ```
41
-
42
- ### Visual comparison with GitHub Desktop
43
-
44
- After committing your work, compare visually:
45
- 1. **Branch** menu → **"Compare to Branch..."**
46
- 2. Select `tutorial/main`
47
- 3. Review differences in the visual interface
48
-
49
- See step 1 for detailed GitHub Desktop instructions.
50
-
51
- ### If you get stuck
52
-
53
- View or reset to the reference:
54
-
55
- ```bash
56
- # View the reducer code from the reference
57
- git show tutorial/main:document-models/chat-room/src/reducers/messages.ts
58
-
59
- # Reset to reference (WARNING: loses your changes)
60
- git reset --hard tutorial/main
61
- ```
62
-
63
- </details>
64
-
65
- In this section, we will implement and test the operation reducers for the **ChatRoom** document model. For this, you have to export the document model specification from the Connect application and import it into your Powerhouse project directory.
66
-
67
- To export the document model specification, follow the steps in the [Define ChatRoom Document Model](/academy/ExampleUsecases/Chatroom/DefineChatroomDocumentModel) section.
68
-
69
- ## Understanding reducers in document models
70
-
71
- Reducers are a core concept in Powerhouse document models. They implement the state transition logic for each operation defined in your schema.
72
-
73
- :::info
74
- **Connection to schema definition language (SDL)**: The reducers directly implement the operations you defined in your SDL. Remember how we defined `AddMessageInput`, `AddEmojiReactionInput`, `RemoveEmojiReactionInput`, `EditChatNameInput`, and `EditChatDescriptionInput` in our schema?
75
- The reducers provide the actual implementation of what happens when those operations are performed.
76
- :::
77
-
78
- To import the document model specification into your Powerhouse project, you can either:
79
-
80
- - Copy and paste the file directly into the root of your Powerhouse project.
81
- - Or drag and drop the file into the Powerhouse project directory in the VSCode editor as seen in the image below:
82
-
83
- Either step will import the document model specification into your Powerhouse project.
14
+ Either step will import the document model into your powerhouse project.
84
15
 
85
16
  ![vscode image](image-4.png)
86
17
 
87
- ## In your project directory
88
-
89
- The next steps will take place in the VSCode editor. Make sure to have it open and the terminal window inside VSCode open as well.
18
+ The next steps will take place in the VSCode editor. Make sure to have it open and the terminal window inside vscode open as well.
90
19
 
91
- To write the operation reducers of the **ChatRoom** document model, you need to generate the document model code from the document model specification file you have exported into the Powerhouse project directory.
20
+ To write the opearation reducers of the `ChatRoom` document model, you need to generate the document model code from the document model file you have exported into the powerhouse project directory.
92
21
 
93
22
  To do this, run the following command in the terminal:
94
23
 
95
24
  ```bash
96
- ph generate ChatRoom.phd
25
+ ph generate ChatRoom.phdm.phd
97
26
  ```
98
27
 
99
- You will see that this action created a range of files for you. Before diving in, let's look at this simple schema to familiarize yourself with the structure you've defined in the document model once more. It shows how each type is connected to the next one.
28
+ You will see that this action created a range of files for you. Before diving in we'll look at this simple schema to make you familiar with the structure you've defined in the document model once more. It shows how each type is connected to the next one.
100
29
 
101
30
  ![Chatroom-demo Schema](image.png)
102
31
 
103
- Now you can navigate to `/document-models/chat-room/src/reducers/messages.ts` and start writing the operation reducers.
32
+ Now you can navigate to `/document-models/chat-room/src/reducers/general-operations.ts` and start writing the operation reducers.
104
33
 
105
- Open the `messages.ts` file and you should see the scaffolding code that needs to be filled for the five operations you have specified earlier. The generated file will look like this:
34
+ Open the `general-operations.ts` file and you should see the code that needs to be filled for the five operations you have defined earlier. Image below shows the code that needs to be filled:
106
35
 
107
- ```typescript
108
- import type { ChatRoomMessagesOperations } from "chatroom/document-models/chat-room";
36
+ ![chatroom ts file](image-5.png)
109
37
 
110
- export const chatRoomMessagesOperations: ChatRoomMessagesOperations = {
111
- addMessageOperation(state, action) {
112
- // TODO: Implement "addMessageOperation" reducer
113
- throw new Error('Reducer "addMessageOperation" not yet implemented');
114
- },
115
- senderOperation(state, action) {
116
- // TODO: Implement "senderOperation" reducer
117
- throw new Error('Reducer "senderOperation" not yet implemented');
118
- },
119
- addEmojiReactionOperation(state, action) {
120
- // TODO: Implement "addEmojiReactionOperation" reducer
121
- throw new Error('Reducer "addEmojiReactionOperation" not yet implemented');
122
- },
123
- removeEmojiReactionOperation(state, action) {
124
- // TODO: Implement "removeEmojiReactionOperation" reducer
125
- throw new Error('Reducer "removeEmojiReactionOperation" not yet implemented');
126
- }
127
- };
128
- ```
38
+ ## Write the Operation Reducers
129
39
 
130
- ## Write the operation reducers
40
+ 1. Copy&paste the code below into the `general-operations.ts` file in the `reducers` folder.
41
+ 2. Save the `general-operations.ts` file.
131
42
 
132
- 1. Copy and paste the code below into the `messages.ts` file in the `reducers` folder.
133
- 2. Save the file.
43
+ ```typescript
44
+ /**
45
+ * This is a scaffold file meant for customization:
46
+ * - modify it by implementing the reducer functions
47
+ * - delete the file and run the code generator again to have it reset
48
+ */
134
49
 
135
- <details>
136
- <summary>Operation Reducers</summary>
50
+ import { MessageContentCannotBeEmpty } from "../../gen/general-operations/error.js";
51
+ import type { ChatRoomGeneralOperationsOperations } from "chatroom/document-models/chat-room";
137
52
 
138
- ```typescript
139
- import {
140
- MessageNotFoundError,
141
- MessageContentCannotBeEmptyError,
142
- } from "../../gen/messages/error.js";
143
- import type { ChatRoomMessagesOperations } from "chatroom-package/document-models/chat-room";
144
-
145
- export const chatRoomMessagesOperations: ChatRoomMessagesOperations = {
146
- addMessageOperation(state, action) {
147
- if (action.input.content === "") {
148
- throw new MessageContentCannotBeEmptyError();
149
- }
150
-
151
- const newMessage = {
152
- id: action.input.messageId,
153
- sender: {
154
- id: action.input.sender.id,
155
- name: action.input.sender.name || null,
156
- avatarUrl: action.input.sender.avatarUrl || null,
157
- },
158
- content: action.input.content,
159
- sentAt: action.input.sentAt,
160
- reactions: [],
161
- };
53
+ export const chatRoomGeneralOperationsOperations: ChatRoomGeneralOperationsOperations =
54
+ {
55
+ addMessageOperation(state, action) {
56
+ if (action.input.content === "") {
57
+ throw new MessageContentCannotBeEmpty();
58
+ }
162
59
 
163
- state.messages.push(newMessage);
164
- },
165
- addEmojiReactionOperation(state, action) {
166
- const message = state.messages.find((m) => m.id === action.input.messageId);
167
- if (!message) {
168
- throw new MessageNotFoundError(
169
- `Message with ID ${action.input.messageId} not found`,
60
+ const newMessage = {
61
+ id: action.input.messageId,
62
+ sender: {
63
+ id: action.input.sender.id,
64
+ name: action.input.sender.name || null,
65
+ avatarUrl: action.input.sender.avatarUrl || null,
66
+ },
67
+ content: action.input.content,
68
+ sentAt: action.input.sentAt,
69
+ reactions: [],
70
+ };
71
+
72
+ state.messages.push(newMessage);
73
+ },
74
+ addEmojiReactionOperation(state, action) {
75
+ const message = state.messages.find(
76
+ (m) => m.id === action.input.messageId,
170
77
  );
171
- }
78
+ if (!message) {
79
+ return state;
80
+ }
172
81
 
173
- if (!message.reactions) {
174
- message.reactions = [];
175
- }
82
+ if (!message.reactions) {
83
+ message.reactions = [];
84
+ }
176
85
 
177
- const existingReaction = message.reactions.find(
178
- (r) => r.type === action.input.type,
179
- );
86
+ const existingReaction = message.reactions.find(
87
+ (r) => r.type === action.input.type,
88
+ );
180
89
 
181
- if (existingReaction) {
182
- if (!existingReaction.reactedBy.includes(action.input.reactedBy)) {
183
- existingReaction.reactedBy.push(action.input.reactedBy);
90
+ if (existingReaction) {
91
+ if (!existingReaction.reactedBy.includes(action.input.reactedBy)) {
92
+ existingReaction.reactedBy.push(action.input.reactedBy);
93
+ }
94
+ } else {
95
+ message.reactions.push({
96
+ type: action.input.type,
97
+ reactedBy: [action.input.reactedBy],
98
+ });
184
99
  }
185
- } else {
186
- message.reactions.push({
187
- type: action.input.type,
188
- reactedBy: [action.input.reactedBy],
189
- });
190
- }
191
- },
192
- removeEmojiReactionOperation(state, action) {
193
- const message = state.messages.find((m) => m.id === action.input.messageId);
194
- if (!message) {
195
- throw new MessageNotFoundError(
196
- `Message with ID ${action.input.messageId} not found`,
100
+ },
101
+ removeEmojiReactionOperation(state, action) {
102
+ const message = state.messages.find(
103
+ (m) => m.id === action.input.messageId,
197
104
  );
198
- }
199
-
200
- if (!message.reactions) {
201
- return;
202
- }
203
-
204
- const reactionIndex = message.reactions.findIndex(
205
- (r) => r.type === action.input.type,
206
- );
207
- if (reactionIndex === -1) {
208
- return;
209
- }
210
-
211
- const reaction = message.reactions[reactionIndex];
212
- const userIndex = reaction.reactedBy.indexOf(action.input.senderId);
213
-
214
- if (userIndex !== -1) {
215
- reaction.reactedBy.splice(userIndex, 1);
105
+ if (!message) {
106
+ return state;
107
+ }
216
108
 
217
- if (reaction.reactedBy.length === 0) {
218
- message.reactions.splice(reactionIndex, 1);
109
+ if (!message.reactions) {
110
+ return;
219
111
  }
220
- }
221
- },
222
- };
223
- ```
224
- </details>
225
112
 
113
+ const reactionIndex = message.reactions.findIndex(
114
+ (r) => r.type === action.input.type,
115
+ );
116
+ if (reactionIndex === -1) {
117
+ return;
118
+ }
226
119
 
227
- 1. Do the same for the reducers of our "settings" operations. Copy and paste the code below into the `settings.ts` file in the `reducers` folder.
228
- 2. Save the file.
120
+ const reaction = message.reactions[reactionIndex];
121
+ const userIndex = reaction.reactedBy.indexOf(action.input.senderId);
229
122
 
123
+ if (userIndex !== -1) {
124
+ reaction.reactedBy.splice(userIndex, 1);
230
125
 
231
- <details>
232
- <summary>Operation Reducers</summary>
126
+ if (reaction.reactedBy.length === 0) {
127
+ message.reactions.splice(reactionIndex, 1);
128
+ }
129
+ }
130
+ },
131
+ editChatNameOperation(state, action) {
132
+ state.name = action.input.name || "";
133
+ },
134
+ editChatDescriptionOperation(state, action) {
135
+ state.description = action.input.description || "";
136
+ },
137
+ };
233
138
 
234
- ```typescript
235
- import type { ChatRoomSettingsOperations } from "chatroom-package/document-models/chat-room";
236
-
237
- export const chatRoomSettingsOperations: ChatRoomSettingsOperations = {
238
- editChatNameOperation(state, action) {
239
- state.name = action.input.name || "";
240
- },
241
- editChatDescriptionOperation(state, action) {
242
- state.description = action.input.description || "";
243
- },
244
- };
245
139
  ```
246
140
 
247
- </details>
141
+ ## Write the Operation Reducers Tests
248
142
 
143
+ In order to make sure the operation reducers are working as expected before implementing an editor interface, you need to write tests for them.
249
144
 
250
- ## Write the operation reducer tests
145
+ The auto generated test will only validate if an action or message in our case is included but will not verify if the reducer mutation is succesfull. This is the type of test you'll have to write as a developer.
251
146
 
252
- In order to make sure the operation reducers are working as expected, you need to write tests for them.
147
+ Navigate to `/document-models/chat-room/src/tests/general-operations.test.ts` and copy&paste the code below into the file. Save the file.
253
148
 
254
- Navigate to `/document-models/chat-room/src/tests` and replace the scaffolding code with comprehensive tests.
255
-
256
- Here are the tests for the five operations implemented in the reducers file. The test file:
257
- - Uses Vitest for testing
258
- - Creates an empty ChatRoom document model
259
- - Tests add message, add/remove reactions, and edit operations
260
- - Verifies both the operation history and the resulting state
261
-
262
- <details>
263
- <summary>Operation Reducer Tests</summary>
149
+ Here are the tests for the five operations written in the reducers file.
264
150
 
265
151
  ```typescript
152
+ /**
153
+ * This is a scaffold file meant for customization:
154
+ * - change it by adding new tests or modifying the existing ones
155
+ */
156
+
266
157
  import { describe, it, expect, beforeEach } from "vitest";
267
158
  import { generateId } from "document-model/core";
268
159
  import {
@@ -290,8 +181,8 @@ describe("GeneralOperations Operations", () => {
290
181
  document = utils.createDocument();
291
182
  });
292
183
 
293
- // Helper function to add a message for testing
294
184
  const addMessageHelper = (): [ChatRoomDocument, AddMessageInput] => {
185
+ // This is a helper function for our upcoming tests
295
186
  const input: AddMessageInput = {
296
187
  content: "Hello, World!",
297
188
  messageId: generateId(),
@@ -308,29 +199,30 @@ describe("GeneralOperations Operations", () => {
308
199
  return [updatedDocument, input];
309
200
  };
310
201
 
311
- // Test adding a new message
312
202
  it("should handle addMessage operation", () => {
313
203
  const [updatedDocument, input] = addMessageHelper();
314
204
 
315
- // Verify the operation was recorded
316
- expect(updatedDocument.operations.global).toHaveLength(1);
317
- expect(updatedDocument.operations.global[0].action.type).toBe("ADD_MESSAGE");
318
- expect(updatedDocument.operations.global[0].action.input).toStrictEqual(input);
205
+ expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the message is being added to the operations history
206
+ expect(updatedDocument.operations.global[0].action.type).toBe(
207
+ "ADD_MESSAGE",
208
+ );
209
+ expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
210
+ input,
211
+ );
319
212
  expect(updatedDocument.operations.global[0].index).toEqual(0);
320
213
 
321
- // Verify the message was added to state
322
- expect(updatedDocument.state.global.messages).toHaveLength(1);
214
+ expect(updatedDocument.state.global.messages).toHaveLength(1); // We're validating that the message is present in the message state of the document
323
215
  expect(updatedDocument.state.global.messages[0]).toMatchObject({
324
216
  id: input.messageId,
325
217
  content: input.content,
326
218
  sender: input.sender,
327
219
  sentAt: input.sentAt,
328
- reactions: [],
220
+ reactions: [], // We also want to make sure that reaction object is an empty array
329
221
  });
330
222
  });
331
223
 
332
- // Test adding an emoji reaction
333
224
  it("should handle addEmojiReaction operation", () => {
225
+ // We're validating that we can react using an emoji with a helper function
334
226
  const [doc, addMessageInput] = addMessageHelper();
335
227
 
336
228
  let updatedDocument = doc;
@@ -346,24 +238,26 @@ describe("GeneralOperations Operations", () => {
346
238
  addEmojiReaction(addEmojiReactionInput),
347
239
  );
348
240
 
349
- // Verify the operation was recorded
350
- expect(updatedDocument.operations.global).toHaveLength(2);
351
- expect(updatedDocument.operations.global[1].action.type).toBe("ADD_EMOJI_REACTION");
241
+ expect(updatedDocument.operations.global).toHaveLength(2); // We're validating that the emoji reaction is added to the operation history of the doc.
242
+ expect(updatedDocument.operations.global[1].action.type).toBe(
243
+ "ADD_EMOJI_REACTION",
244
+ );
352
245
  expect(updatedDocument.operations.global[1].action.input).toStrictEqual(
353
246
  addEmojiReactionInput,
354
247
  );
355
248
  expect(updatedDocument.operations.global[1].index).toEqual(1);
356
249
 
357
- // Verify the reaction was added
358
- expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(1);
359
- expect(updatedDocument.state.global.messages[0].reactions?.[0]).toMatchObject({
360
- reactedBy: [addEmojiReactionInput.reactedBy],
250
+ expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(1); // We're validating that the message we created has only one reaction
251
+ expect(
252
+ updatedDocument.state.global.messages[0].reactions?.[0],
253
+ ).toMatchObject({
254
+ reactedBy: [addEmojiReactionInput.reactedBy], // We're validating that reactedBy object only contains the right address
361
255
  type: addEmojiReactionInput.type,
362
256
  });
363
257
  });
364
258
 
365
- // Test adding reaction to non-existing message
366
259
  it("should handle addEmojiReaction operation to a non existing message", () => {
260
+ // We're testing that the state doesn't change when reacting to a non-existing message
367
261
  const input: AddEmojiReactionInput = {
368
262
  messageId: "invalid-message-id",
369
263
  reactedBy: "anon-user",
@@ -373,12 +267,14 @@ describe("GeneralOperations Operations", () => {
373
267
  const updatedDocument = reducer(document, addEmojiReaction(input));
374
268
 
375
269
  expect(updatedDocument.operations.global).toHaveLength(1);
376
- expect(updatedDocument.operations.global[0].action.type).toBe("ADD_EMOJI_REACTION");
270
+ expect(updatedDocument.operations.global[0].action.type).toBe(
271
+ "ADD_EMOJI_REACTION",
272
+ );
377
273
  expect(updatedDocument.state.global.messages).toHaveLength(0);
378
274
  });
379
275
 
380
- // Test removing an emoji reaction
381
276
  it("should handle removeEmojiReaction operation", () => {
277
+ // We're making use of a helper function to check if we can remove an EmojiReaction
382
278
  const [doc, addMessageInput] = addMessageHelper();
383
279
 
384
280
  let updatedDocument = doc;
@@ -395,6 +291,7 @@ describe("GeneralOperations Operations", () => {
395
291
  );
396
292
 
397
293
  const input: RemoveEmojiReactionInput = {
294
+ // We're validating the removal of a message by our anon-user with a specific messageId
398
295
  messageId: addMessageInput.messageId,
399
296
  senderId: "anon-user",
400
297
  type: "THUMBS_UP",
@@ -402,17 +299,19 @@ describe("GeneralOperations Operations", () => {
402
299
 
403
300
  updatedDocument = reducer(updatedDocument, removeEmojiReaction(input));
404
301
 
405
- // Verify the operation was recorded
406
- expect(updatedDocument.operations.global).toHaveLength(3);
407
- expect(updatedDocument.operations.global[2].action.type).toBe("REMOVE_EMOJI_REACTION");
408
- expect(updatedDocument.operations.global[2].action.input).toStrictEqual(input);
302
+ expect(updatedDocument.operations.global).toHaveLength(3); // We're validating that the operation was added to the operation history.
303
+ expect(updatedDocument.operations.global[2].action.type).toBe(
304
+ "REMOVE_EMOJI_REACTION",
305
+ );
306
+ expect(updatedDocument.operations.global[2].action.input).toStrictEqual(
307
+ input,
308
+ );
409
309
  expect(updatedDocument.operations.global[2].index).toEqual(2);
410
310
 
411
- // Verify the reaction was removed
311
+ // When the last user removes their reaction, the entire reaction should be removed
412
312
  expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(0);
413
313
  });
414
314
 
415
- // Test editing chat name
416
315
  it("should handle editChatName operation", () => {
417
316
  const input: EditChatNameInput = {
418
317
  name: "New Chat Name",
@@ -420,17 +319,18 @@ describe("GeneralOperations Operations", () => {
420
319
 
421
320
  const updatedDocument = reducer(document, editChatName(input));
422
321
 
423
- // Verify the operation was recorded
424
- expect(updatedDocument.operations.global).toHaveLength(1);
425
- expect(updatedDocument.operations.global[0].action.type).toBe("EDIT_CHAT_NAME");
426
- expect(updatedDocument.operations.global[0].action.input).toStrictEqual(input);
322
+ expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
323
+ expect(updatedDocument.operations.global[0].action.type).toBe(
324
+ "EDIT_CHAT_NAME",
325
+ );
326
+ expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
327
+ input,
328
+ );
427
329
  expect(updatedDocument.operations.global[0].index).toEqual(0);
428
330
 
429
- // Verify the name was updated
430
331
  expect(updatedDocument.state.global.name).toBe(input.name);
431
332
  });
432
333
 
433
- // Test editing chat description
434
334
  it("should handle editChatDescription operation", () => {
435
335
  const input: EditChatDescriptionInput = {
436
336
  description: "New Chat Description",
@@ -438,55 +338,34 @@ describe("GeneralOperations Operations", () => {
438
338
 
439
339
  const updatedDocument = reducer(document, editChatDescription(input));
440
340
 
441
- // Verify the operation was recorded
442
- expect(updatedDocument.operations.global).toHaveLength(1);
443
- expect(updatedDocument.operations.global[0].action.type).toBe("EDIT_CHAT_DESCRIPTION");
444
- expect(updatedDocument.operations.global[0].action.input).toStrictEqual(input);
341
+ expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
342
+ expect(updatedDocument.operations.global[0].action.type).toBe(
343
+ "EDIT_CHAT_DESCRIPTION",
344
+ );
345
+ expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
346
+ input,
347
+ );
445
348
  expect(updatedDocument.operations.global[0].index).toEqual(0);
446
349
 
447
- // Verify the description was updated
448
350
  expect(updatedDocument.state.global.description).toBe(input.description);
449
351
  });
450
352
  });
451
353
  ```
452
354
 
453
- </details>
454
-
455
355
  Now you can run the tests to make sure the operation reducers are working as expected.
456
356
 
457
357
  ```bash
458
- pnpm run test
358
+ npm run test
459
359
  ```
460
360
 
461
361
  Output should be as follows:
462
362
 
463
363
  ```bash
464
- ✓ document-models/chat-room/src/tests/document-model.test.ts (3 tests) 1ms
465
- ✓ document-models/chat-room/src/tests/general-operations.test.ts (6 tests) 8ms
466
-
467
364
  Test Files 2 passed (2)
468
- Tests 9 passed (9)
365
+ Tests 7 passed (7)
469
366
  Start at 15:19:52
470
367
  Duration 3.61s (transform 77ms, setup 0ms, collect 3.50s, tests 14ms, environment 0ms, prepare 474ms)
471
368
  ```
472
369
 
473
- If you got the same output, you have successfully implemented the operation reducers and tests for the **ChatRoom** document model.
474
-
475
- ## Compare with reference implementation
476
-
477
- Verify your implementation matches the tutorial:
478
-
479
- ```bash
480
- # View reference reducer implementation
481
- git show tutorial/main:document-models/chat-room/src/reducers/general-operations.ts
482
-
483
- # View reference test implementation
484
- git show tutorial/main:document-models/chat-room/src/tests/general-operations.test.ts
485
-
486
- # Compare your entire implementation
487
- git diff tutorial/main -- document-models/chat-room/src/
488
- ```
489
-
490
- ## Up next: ChatRoom editor
491
-
492
- Continue to the next section to learn how to implement the document model editor so you can see a simple user interface for the **ChatRoom** document model in action.
370
+ If you got the same output, you have successfully implemented the operation reducers and tests for the `ChatRoom` document model.
371
+ Continue to the next section to learn how to implement the document model editor so you can see a simple user interface for the `ChatRoom` document model in action.