@powerhousedao/academy 5.0.3 → 5.0.5

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 (28) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/docs/academy/00-EthereumArgentinaHackathon.md +207 -0
  3. package/docs/academy/01-GetStarted/00-ExploreDemoPackage.mdx +1 -1
  4. package/docs/academy/01-GetStarted/05-VetraStudio.md +76 -27
  5. package/docs/academy/01-GetStarted/home.mdx +4 -4
  6. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +24 -8
  7. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +25 -7
  8. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +17 -3
  9. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +5 -2
  10. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +7 -1
  11. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +5 -5
  12. package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +4 -1
  13. package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +21 -9
  14. package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +132 -129
  15. package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +50 -44
  16. package/docs/academy/03-ExampleUsecases/Chatroom/image-4.png +0 -0
  17. package/docs/academy/03-ExampleUsecases/Chatroom/image-5.png +0 -0
  18. package/docs/academy/03-ExampleUsecases/Chatroom/images/ChatRoomConnectApp.gif +0 -0
  19. package/docs/academy/03-ExampleUsecases/Chatroom/images/ChatRoomTest.gif +0 -0
  20. package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +39 -5
  21. package/docs/academy/05-Architecture/01-WorkingWithTheReactor.md +0 -2
  22. package/package.json +1 -1
  23. package/sidebars.ts +8 -0
  24. package/src/css/custom.css +20 -0
  25. package/static/img/ethereum-logo.jpeg +0 -0
  26. package/docs/academy/09-AIResources +0 -131
  27. package/docs/academy/TUTORIAL_VERIFICATION_ARCHITECTURE +0 -325
  28. /package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/{02-RevisionHistoryTimeline.md → 02-RevisionHistoryTimeline} +0 -0
@@ -112,6 +112,12 @@ Now, run the tests from your project's root directory to verify your implementat
112
112
  pnpm run test
113
113
  ```
114
114
 
115
+ Or with npm:
116
+
117
+ ```bash
118
+ npm test
119
+ ```
120
+
115
121
  If all tests pass, you have successfully verified the core logic of your `To-do List` document model. This ensures that the reducers you wrote behave exactly as expected.
116
122
 
117
123
  </details>
@@ -128,7 +134,7 @@ While the tutorial provides a concrete example, keep these general best practice
128
134
  - **Assert**: Check if the outcome is as expected using `expect()`.
129
135
  - **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);`.
130
136
  - **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.
131
- - **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.
137
+ - **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` (or `npm test`) command is your friend.
132
138
 
133
139
  ## Conclusion: The payoff of diligent testing
134
140
 
@@ -2,16 +2,16 @@
2
2
 
3
3
  :::info
4
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.
5
+ The Todo-demo 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
6
 
7
- https://github.com/powerhouse-inc/todo-demo-package
7
+ https://github.com/powerhouse-inc/todo-demo
8
8
  :::
9
9
 
10
10
  There are several ways to explore this package:
11
11
 
12
- ### Option 1: Rebuild the Todo-demo-package
12
+ ### Option 1: Rebuild the Todo-demo
13
13
 
14
- The Todo-demo-package and repository are your main reference points during the Mastery Track.
14
+ The Todo-demo and repository are your main reference points during the Mastery Track.
15
15
  Follow the steps in the "Mastery Track – Document Model Creation" chapters to build along with the examples.
16
16
 
17
17
  ### Option 2: Clone and run the code locally
@@ -35,5 +35,5 @@ ph connect
35
35
  Alternatively, you can install this package in a Powerhouse project or in your deployed host apps:
36
36
 
37
37
  ```bash
38
- ph install @powerhousedao/todo-demo-package
38
+ ph install @powerhousedao/todo-demo
39
39
  ```
@@ -73,7 +73,10 @@ Watching local document editors at '/home/you/Powerhouse/ChatRoom/editors'...
73
73
 
74
74
  A new browser window will open and you will see the Connect application. If it doesn't open automatically, you can open it manually by navigating to `http://localhost:3000/` in your browser.
75
75
 
76
- Create a new document model by clicking on the `DocumentModel` button by the "New Document" section. The Gif below shows you where to click.
76
+ If you don't have a local drive created yet, create one using the Generic Drive Explorer app.
77
+
78
+ Open your Local Drive and create a new document model by clicking the `DocumentModel` button in the "New Document" section. The GIF below shows where to click.
79
+
77
80
 
78
81
  ![Create New Document Model](./images/ChatRoomConnectApp.gif)
79
82
 
@@ -65,11 +65,17 @@ enum ReactionType {
65
65
 
66
66
  input AddMessageInput {
67
67
  messageId: OID! # ID of the message that is being added
68
- sender: Sender! # ID of the user sending the message
68
+ sender: SenderInput! # ID of the user sending the message
69
69
  content: String! # Content of the message
70
70
  sentAt: DateTime!
71
71
  }
72
72
 
73
+ input SenderInput {
74
+ id: ID! # Unique identifier for the sender
75
+ name: String
76
+ avatarUrl: URL # Allows us to pull the ENS and/or nft of the persons profile
77
+ }
78
+
73
79
  input AddEmojiReactionInput {
74
80
  messageId: OID! # ID of the message to which the reaction is being added
75
81
  reactedBy: ID! # ID of the user adding the reaction
@@ -106,14 +112,14 @@ The steps below show you how to do this:
106
112
 
107
113
  3. In the code editor, you can see the SDL for the document model. Replace the existing SDL with the SDL defined in the [State Schema](#state-schema) section above. Only copy and paste the types, leaving the inputs for the next step. You can however already press 'Sync with schema' button to set the initial state of your document model based on your Schema Definition Language. Verify that your Global State Initial Value looks like this.
108
114
 
109
- ```graphql
115
+ ```json
110
116
  {
111
117
  "id": "",
112
118
  "name": "",
113
- "description": "",
114
- "createdAt": "",
115
- "createdBy": "",
116
- "messages": ""
119
+ "description": null,
120
+ "createdAt": null,
121
+ "createdBy": null,
122
+ "messages": []
117
123
  }
118
124
  ```
119
125
 
@@ -123,11 +129,17 @@ The steps below show you how to do this:
123
129
 
124
130
  ```graphql
125
131
  input AddMessageInput {
126
- messageId: OID! # ID of the message that is being added
127
- sender: Sender! # ID of the user sending the message
128
- content: String! # Content of the message
132
+ messageId: OID!
133
+ sender: SenderInput!
134
+ content: String!
129
135
  sentAt: DateTime!
130
136
  }
137
+
138
+ input SenderInput {
139
+ id: ID! # Unique identifier for the sender
140
+ name: String
141
+ avatarUrl: URL # Allows us to pull the ENS and/or nft of the persons profile
142
+ }
131
143
  ```
132
144
 
133
145
  7. Repeat step 6 for the other input operations based on the [Operations Schema](#operations-schema). If you noticed, you only need to add the name `(ADD_EMOJI_REACTION, EDIT_CHAT_NAME, etc)` of the operation without the `input` suffix. Then it will be generated once you press enter.
@@ -22,7 +22,7 @@ To write the opearation reducers of the `ChatRoom` document model, you need to g
22
22
  To do this, run the following command in the terminal:
23
23
 
24
24
  ```bash
25
- ph generate ChatRoom.phdm.zip
25
+ ph generate ChatRoom.phdm.phd
26
26
  ```
27
27
 
28
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.
@@ -47,97 +47,95 @@ Open the `general-operations.ts` file and you should see the code that needs to
47
47
  * - delete the file and run the code generator again to have it reset
48
48
  */
49
49
 
50
- import { ChatRoomAddMessageOperations } from "../../gen/add-message/operations";
51
- import { MessageContentCannotBeEmpty } from "../../gen/add-message/error";
52
-
53
- export const reducer: ChatRoomAddMessageOperations = {
54
- addMessageOperation(state, action, dispatch) {
55
- if (action.input.content === "") {
56
- throw new MessageContentCannotBeEmpty(); // Your reducer exception is used here as a custom error.
57
- }
58
-
59
- state.messages.push({
60
- id: action.input.messageId,
61
- content: action.input.content,
62
- sender: action.input.sender,
63
- sentAt: action.input.sentAt,
64
- reactions: [],
65
- });
66
- },
67
- addEmojiReactionOperation(state, action, dispatch) {
68
- const message = state.messages.find(
69
- (message) => message.id === action.input.messageId, // the reducer checks the existence of the message you want to react to.
70
- );
50
+ import { MessageContentCannotBeEmpty } from "../../gen/general-operations/error.js";
51
+ import type { ChatRoomGeneralOperationsOperations } from "chatroom/document-models/chat-room";
71
52
 
72
- if (!message) {
73
- throw new Error("Message not found");
74
- }
53
+ export const chatRoomGeneralOperationsOperations: ChatRoomGeneralOperationsOperations =
54
+ {
55
+ addMessageOperation(state, action) {
56
+ if (action.input.content === "") {
57
+ throw new MessageContentCannotBeEmpty();
58
+ }
75
59
 
76
- const reactions = message.reactions || [];
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,
77
+ );
78
+ if (!message) {
79
+ return state;
80
+ }
77
81
 
78
- const existingReaction = reactions.find(
79
- (reaction) => reaction.type === action.input.type,
80
- );
82
+ if (!message.reactions) {
83
+ message.reactions = [];
84
+ }
81
85
 
82
- if (existingReaction) {
83
- // if the message reaction exists a new reactedBy gets added.
84
- message.reactions = reactions.map((reaction) => {
85
- if (reaction.type === action.input.type) {
86
- return {
87
- ...reaction,
88
- reactedBy: [...reaction.reactedBy, action.input.reactedBy],
89
- };
90
- }
86
+ const existingReaction = message.reactions.find(
87
+ (r) => r.type === action.input.type,
88
+ );
91
89
 
92
- return reaction;
93
- });
94
- } else {
95
- message.reactions = [
96
- ...reactions,
97
- {
98
- reactedBy: [action.input.reactedBy], // if the message reaction doesn't exist yet a new reaction gets created
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({
99
96
  type: action.input.type,
100
- },
101
- ];
102
- }
97
+ reactedBy: [action.input.reactedBy],
98
+ });
99
+ }
100
+ },
101
+ removeEmojiReactionOperation(state, action) {
102
+ const message = state.messages.find(
103
+ (m) => m.id === action.input.messageId,
104
+ );
105
+ if (!message) {
106
+ return state;
107
+ }
103
108
 
104
- state.messages = state.messages.map((_message) => {
105
- // the state of the chatroom documents messages gets updated
106
- if (_message.id === message.id) {
107
- return message;
109
+ if (!message.reactions) {
110
+ return;
108
111
  }
109
112
 
110
- return _message;
111
- });
112
- },
113
- removeEmojiReactionOperation(state, action, dispatch) {
114
- // To remove a reaction the address is removed from the reactedBy object in the reactions type.
115
- state.messages = state.messages.map((message) => {
116
- if (message.id === action.input.messageId) {
117
- message.reactions = (message.reactions || []).map((reaction) => {
118
- if (reaction.type === action.input.type) {
119
- return {
120
- ...reaction,
121
- reactedBy: reaction.reactedBy.filter(
122
- (reactedBy) => reactedBy !== action.input.senderId, // We're removing the sender of the reaction from the the reactedBy object
123
- ),
124
- };
125
- }
126
-
127
- return reaction;
128
- });
113
+ const reactionIndex = message.reactions.findIndex(
114
+ (r) => r.type === action.input.type,
115
+ );
116
+ if (reactionIndex === -1) {
117
+ return;
129
118
  }
130
119
 
131
- return message; // We're updating the document state with our changes
132
- });
133
- },
134
- editChatNameOperation(state, action, dispatch) {
135
- state.name = action.input.name || "";
136
- },
137
- editChatDescriptionOperation(state, action, dispatch) {
138
- state.description = action.input.description || "";
139
- },
140
- };
120
+ const reaction = message.reactions[reactionIndex];
121
+ const userIndex = reaction.reactedBy.indexOf(action.input.senderId);
122
+
123
+ if (userIndex !== -1) {
124
+ reaction.reactedBy.splice(userIndex, 1);
125
+
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
+ };
138
+
141
139
  ```
142
140
 
143
141
  ## Write the Operation Reducers Tests
@@ -156,34 +154,38 @@ Here are the tests for the five operations written in the reducers file.
156
154
  * - change it by adding new tests or modifying the existing ones
157
155
  */
158
156
 
159
- import { generateMock } from "@powerhousedao/codegen";
160
- import { utils as documentModelUtils } from "document-model/document";
161
-
162
- import utils from "../../gen/utils";
157
+ import { describe, it, expect, beforeEach } from "vitest";
158
+ import { generateId } from "document-model/core";
163
159
  import {
164
- z,
160
+ reducer,
161
+ utils,
162
+ addMessage,
163
+ addEmojiReaction,
164
+ removeEmojiReaction,
165
+ editChatName,
166
+ editChatDescription,
167
+ } from "../../gen/index.js";
168
+ import type {
169
+ ChatRoomDocument,
165
170
  AddMessageInput,
166
171
  AddEmojiReactionInput,
167
172
  RemoveEmojiReactionInput,
168
173
  EditChatNameInput,
169
174
  EditChatDescriptionInput,
170
- } from "../../gen/schema";
171
- import { reducer } from "../../gen/reducer";
172
- import * as creators from "../../gen/add-message/creators";
173
- import { ChatRoomDocument } from "../../gen/types";
175
+ } from "../../gen/types.js";
174
176
 
175
- describe("AddMessage Operations", () => {
177
+ describe("GeneralOperations Operations", () => {
176
178
  let document: ChatRoomDocument;
177
179
 
178
180
  beforeEach(() => {
179
181
  document = utils.createDocument();
180
182
  });
181
183
 
182
- const addMessage = (): [ChatRoomDocument, AddMessageInput] => {
183
- // This is a helper function for our upcoming test
184
+ const addMessageHelper = (): [ChatRoomDocument, AddMessageInput] => {
185
+ // This is a helper function for our upcoming tests
184
186
  const input: AddMessageInput = {
185
187
  content: "Hello, World!",
186
- messageId: documentModelUtils.hashKey(),
188
+ messageId: generateId(),
187
189
  sender: {
188
190
  id: "anon-user",
189
191
  name: null,
@@ -192,17 +194,21 @@ describe("AddMessage Operations", () => {
192
194
  sentAt: new Date().toISOString(),
193
195
  };
194
196
 
195
- const updatedDocument = reducer(document, creators.addMessage(input));
197
+ const updatedDocument = reducer(document, addMessage(input));
196
198
 
197
199
  return [updatedDocument, input];
198
200
  };
199
201
 
200
202
  it("should handle addMessage operation", () => {
201
- const [updatedDocument, input] = addMessage();
203
+ const [updatedDocument, input] = addMessageHelper();
202
204
 
203
205
  expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the message is being added to the operations history
204
- expect(updatedDocument.operations.global[0].type).toBe("ADD_MESSAGE");
205
- expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
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
+ );
206
212
  expect(updatedDocument.operations.global[0].index).toEqual(0);
207
213
 
208
214
  expect(updatedDocument.state.global.messages).toHaveLength(1); // We're validating that the message is present in the message state of the document
@@ -217,7 +223,7 @@ describe("AddMessage Operations", () => {
217
223
 
218
224
  it("should handle addEmojiReaction operation", () => {
219
225
  // We're validating that we can react using an emoji with a helper function
220
- const [doc, addMessageInput] = addMessage();
226
+ const [doc, addMessageInput] = addMessageHelper();
221
227
 
222
228
  let updatedDocument = doc;
223
229
 
@@ -229,14 +235,14 @@ describe("AddMessage Operations", () => {
229
235
 
230
236
  updatedDocument = reducer(
231
237
  updatedDocument,
232
- creators.addEmojiReaction(addEmojiReactionInput),
238
+ addEmojiReaction(addEmojiReactionInput),
233
239
  );
234
240
 
235
241
  expect(updatedDocument.operations.global).toHaveLength(2); // We're validating that the emoji reaction is added to the operation history of the doc.
236
- expect(updatedDocument.operations.global[1].type).toBe(
242
+ expect(updatedDocument.operations.global[1].action.type).toBe(
237
243
  "ADD_EMOJI_REACTION",
238
244
  );
239
- expect(updatedDocument.operations.global[1].input).toStrictEqual(
245
+ expect(updatedDocument.operations.global[1].action.input).toStrictEqual(
240
246
  addEmojiReactionInput,
241
247
  );
242
248
  expect(updatedDocument.operations.global[1].index).toEqual(1);
@@ -251,28 +257,25 @@ describe("AddMessage Operations", () => {
251
257
  });
252
258
 
253
259
  it("should handle addEmojiReaction operation to a non existing message", () => {
254
- // We're testing that an error is thrown when reacting to a non-existing message
260
+ // We're testing that the state doesn't change when reacting to a non-existing message
255
261
  const input: AddEmojiReactionInput = {
256
262
  messageId: "invalid-message-id",
257
263
  reactedBy: "anon-user",
258
264
  type: "THUMBS_UP",
259
265
  };
260
266
 
261
- const updatedDocument = reducer(document, creators.addEmojiReaction(input));
267
+ const updatedDocument = reducer(document, addEmojiReaction(input));
262
268
 
263
269
  expect(updatedDocument.operations.global).toHaveLength(1);
264
- expect(updatedDocument.operations.global[0].type).toBe(
270
+ expect(updatedDocument.operations.global[0].action.type).toBe(
265
271
  "ADD_EMOJI_REACTION",
266
272
  );
267
- expect(updatedDocument.operations.global[0].error).toBe(
268
- "Message not found",
269
- );
270
273
  expect(updatedDocument.state.global.messages).toHaveLength(0);
271
274
  });
272
275
 
273
276
  it("should handle removeEmojiReaction operation", () => {
274
277
  // We're making use of a helper function to check if we can remove an EmojiReaction
275
- const [doc, addMessageInput] = addMessage();
278
+ const [doc, addMessageInput] = addMessageHelper();
276
279
 
277
280
  let updatedDocument = doc;
278
281
 
@@ -284,7 +287,7 @@ describe("AddMessage Operations", () => {
284
287
 
285
288
  updatedDocument = reducer(
286
289
  updatedDocument,
287
- creators.addEmojiReaction(addEmojiReactionInput),
290
+ addEmojiReaction(addEmojiReactionInput),
288
291
  );
289
292
 
290
293
  const input: RemoveEmojiReactionInput = {
@@ -294,22 +297,19 @@ describe("AddMessage Operations", () => {
294
297
  type: "THUMBS_UP",
295
298
  };
296
299
 
297
- updatedDocument = reducer(
298
- updatedDocument,
299
- creators.removeEmojiReaction(input),
300
- );
300
+ updatedDocument = reducer(updatedDocument, removeEmojiReaction(input));
301
301
 
302
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].type).toBe(
303
+ expect(updatedDocument.operations.global[2].action.type).toBe(
304
304
  "REMOVE_EMOJI_REACTION",
305
305
  );
306
- expect(updatedDocument.operations.global[2].input).toStrictEqual(input);
306
+ expect(updatedDocument.operations.global[2].action.input).toStrictEqual(
307
+ input,
308
+ );
307
309
  expect(updatedDocument.operations.global[2].index).toEqual(2);
308
310
 
309
- expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(1); // The reaction should still exist but no-one should have reacted to it
310
- expect(
311
- updatedDocument.state.global.messages[0].reactions?.[0]?.reactedBy,
312
- ).toHaveLength(0);
311
+ // When the last user removes their reaction, the entire reaction should be removed
312
+ expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(0);
313
313
  });
314
314
 
315
315
  it("should handle editChatName operation", () => {
@@ -317,11 +317,15 @@ describe("AddMessage Operations", () => {
317
317
  name: "New Chat Name",
318
318
  };
319
319
 
320
- const updatedDocument = reducer(document, creators.editChatName(input));
320
+ const updatedDocument = reducer(document, editChatName(input));
321
321
 
322
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].type).toBe("EDIT_CHAT_NAME");
324
- expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
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
+ );
325
329
  expect(updatedDocument.operations.global[0].index).toEqual(0);
326
330
 
327
331
  expect(updatedDocument.state.global.name).toBe(input.name);
@@ -332,16 +336,15 @@ describe("AddMessage Operations", () => {
332
336
  description: "New Chat Description",
333
337
  };
334
338
 
335
- const updatedDocument = reducer(
336
- document,
337
- creators.editChatDescription(input),
338
- );
339
+ const updatedDocument = reducer(document, editChatDescription(input));
339
340
 
340
341
  expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
341
- expect(updatedDocument.operations.global[0].type).toBe(
342
+ expect(updatedDocument.operations.global[0].action.type).toBe(
342
343
  "EDIT_CHAT_DESCRIPTION",
343
344
  );
344
- expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
345
+ expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
346
+ input,
347
+ );
345
348
  expect(updatedDocument.operations.global[0].index).toEqual(0);
346
349
 
347
350
  expect(updatedDocument.state.global.description).toBe(input.description);