@@ -7,8 +7,8 @@ Please see LICENSE files in the repository root for full details.
77
88import React from "react" ;
99import { mocked , type MockedObject } from "jest-mock" ;
10- import { type MatrixClient , MatrixEvent , Room } from "matrix-js-sdk/src/matrix" ;
11- import { render , cleanup , screen , fireEvent } from "jest-matrix-react" ;
10+ import { type MatrixClient , MatrixEvent , Preset , Room } from "matrix-js-sdk/src/matrix" ;
11+ import { render , cleanup , screen , fireEvent , waitFor , act } from "jest-matrix-react" ;
1212
1313import { stubClient , mockPlatformPeg , unmockPlatformPeg , withClientContextRenderOptions } from "../../../test-utils" ;
1414import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases" ;
@@ -17,6 +17,8 @@ import ResizeNotifier from "../../../../src/utils/ResizeNotifier.ts";
1717import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks.ts" ;
1818import RightPanelStore from "../../../../src/stores/right-panel/RightPanelStore.ts" ;
1919import DMRoomMap from "../../../../src/utils/DMRoomMap.ts" ;
20+ import { type IOpts } from "../../../../src/createRoom.ts" ;
21+ import SpaceStore from "../../../../src/stores/spaces/SpaceStore.ts" ;
2022
2123describe ( "SpaceRoomView" , ( ) => {
2224 let cli : MockedObject < MatrixClient > ;
@@ -86,7 +88,7 @@ describe("SpaceRoomView", () => {
8688 cleanup ( ) ;
8789 } ) ;
8890
89- const renderSpaceRoomView = async ( ) : Promise < ReturnType < typeof render > > => {
91+ const renderSpaceRoomView = async ( justCreatedOpts ?: IOpts ) : Promise < ReturnType < typeof render > > => {
9092 const resizeNotifier = new ResizeNotifier ( ) ;
9193 const permalinkCreator = new RoomPermalinkCreator ( space ) ;
9294
@@ -97,6 +99,7 @@ describe("SpaceRoomView", () => {
9799 permalinkCreator = { permalinkCreator }
98100 onJoinButtonClicked = { jest . fn ( ) }
99101 onRejectButtonClicked = { jest . fn ( ) }
102+ justCreatedOpts = { justCreatedOpts }
100103 /> ,
101104 withClientContextRenderOptions ( cli ) ,
102105 ) ;
@@ -113,5 +116,175 @@ describe("SpaceRoomView", () => {
113116
114117 expect ( spy ) . toHaveBeenCalledWith ( { phase : RightPanelPhases . MemberList } ) ;
115118 } ) ;
119+
120+ it ( "shows SpaceLandingAddButton context menu when Add button is clicked" , async ( ) => {
121+ await renderSpaceRoomView ( ) ;
122+ await expect ( screen . findByText ( "Welcome to" ) ) . resolves . toBeVisible ( ) ;
123+
124+ const addButton = screen . getByRole ( "button" , { name : / a d d / i } ) ;
125+ fireEvent . click ( addButton ) ;
126+
127+ expect ( await screen . findByText ( / n e w r o o m / i) ) . toBeInTheDocument ( ) ;
128+ expect ( screen . getByText ( / a d d e x i s t i n g r o o m / i) ) . toBeInTheDocument ( ) ;
129+ } ) ;
130+ } ) ;
131+
132+ describe ( "Spaces: creating a new community space" , ( ) => {
133+ it ( "asks what topics you want to discuss, creates rooms for them and offers to share" , async ( ) => {
134+ cli . createRoom . mockResolvedValueOnce ( { room_id : "room1" } ) . mockResolvedValueOnce ( { room_id : "room2" } ) ;
135+ SpaceStore . instance . addRoomToSpace = jest . fn ( ) ;
136+
137+ // Given we are creating a space
138+ const view = await renderSpaceRoomView ( {
139+ createOpts : { preset : Preset . PublicChat } ,
140+ name : "My MySpace Space" ,
141+ } ) ;
142+
143+ // Then we are asked what topics we want
144+ expect (
145+ view . getByRole ( "heading" , { name : "What are some things you want to discuss in My MySpace Space?" } ) ,
146+ ) . toBeInTheDocument ( ) ;
147+
148+ // And some defaults are suggested
149+ expect ( view . getByPlaceholderText ( / g e n e r a l / i) ) . toBeInTheDocument ( ) ;
150+ expect ( view . getByPlaceholderText ( / r a n d o m / i) ) . toBeInTheDocument ( ) ;
151+ expect ( view . getByPlaceholderText ( / s u p p o r t / i) ) . toBeInTheDocument ( ) ;
152+
153+ // When we enter some room names
154+ const input1 = view . getAllByRole ( "textbox" ) [ 0 ] ;
155+ const input2 = view . getAllByRole ( "textbox" ) [ 1 ] ;
156+ fireEvent . change ( input1 , { target : { value : "Room 1" } } ) ;
157+ fireEvent . change ( input2 , { target : { value : "Room 2" } } ) ;
158+
159+ // And click "Continue"
160+ const button = view . getByRole ( "button" , { name : "Continue" } ) ;
161+ fireEvent . click ( button ) ;
162+
163+ // Then we create 2 rooms
164+ await waitFor ( ( ) => {
165+ expect ( cli . createRoom ) . toHaveBeenCalledTimes ( 2 ) ;
166+ } ) ;
167+
168+ // And offer the user to share this space
169+ await waitFor ( ( ) =>
170+ expect ( view . getByRole ( "heading" , { name : "Share My MySpace Space" } ) ) . toBeInTheDocument ( ) ,
171+ ) ;
172+ expect ( view . getByRole ( "button" , { name : / S h a r e i n v i t e l i n k / } ) ) . toBeInTheDocument ( ) ;
173+
174+ // And allow them to continue to the first room
175+ expect ( view . getByRole ( "button" , { name : "Go to my first room" } ) ) . toBeInTheDocument ( ) ;
176+ } ) ;
177+
178+ it ( "shows 'Skip for now' when all fields are empty, 'Continue' when any field is filled" , async ( ) => {
179+ // Given we are creating a space
180+ const view = await renderSpaceRoomView ( {
181+ createOpts : { preset : Preset . PublicChat } ,
182+ } ) ;
183+
184+ // When we clear all the topics
185+ view . getAllByRole ( "textbox" ) . forEach ( ( input ) => fireEvent . change ( input , { target : { value : "" } } ) ) ;
186+
187+ // Then the button reads "Skip for now"
188+ expect ( view . getByRole ( "button" , { name : "Skip for now" } ) ) . toBeVisible ( ) ;
189+
190+ // But when we enter a topic
191+ fireEvent . change ( view . getAllByRole ( "textbox" ) [ 0 ] , { target : { value : "Room" } } ) ;
192+
193+ // Then the button says "Continue"
194+ expect ( view . getByRole ( "button" , { name : "Continue" } ) ) . toBeVisible ( ) ;
195+ } ) ;
196+
197+ it ( "shows error message if room creation fails" , async ( ) => {
198+ // Given we are creating a space
199+ const view = await renderSpaceRoomView ( {
200+ createOpts : { preset : Preset . PublicChat } ,
201+ } ) ;
202+
203+ // And when we create a room it will fail
204+ cli . createRoom . mockRejectedValue ( new Error ( "fail" ) ) ;
205+
206+ // When we create the space
207+ fireEvent . change ( view . getAllByRole ( "textbox" ) [ 0 ] , { target : { value : "Room A" } } ) ;
208+ fireEvent . click ( view . getByRole ( "button" , { name : "Continue" } ) ) ;
209+
210+ // Then we display an error message because it failed
211+ await waitFor ( ( ) => {
212+ expect (
213+ view . getByText ( ( content ) => content . toLowerCase ( ) . includes ( "failed to create initial space rooms" ) ) ,
214+ ) . toBeInTheDocument ( ) ;
215+ } ) ;
216+ } ) ;
217+
218+ it ( "disables button and shows 'Creating rooms' while busy" , async ( ) => {
219+ // Given we are creating a space
220+ const view = await renderSpaceRoomView ( {
221+ createOpts : { preset : Preset . PublicChat } ,
222+ } ) ;
223+
224+ // And creating a room will be slow
225+ cli . createRoom . mockImplementation (
226+ ( ) =>
227+ new Promise ( ( ) => {
228+ // This promise never resolves
229+ } ) ,
230+ ) ;
231+
232+ // When we create the space
233+ fireEvent . change ( view . getAllByRole ( "textbox" ) [ 0 ] , { target : { value : "Room A" } } ) ;
234+ fireEvent . click ( view . getByRole ( "button" , { name : "Continue" } ) ) ;
235+
236+ // Then the "Creating rooms..." message is displayed
237+ const button = view . getByRole ( "button" ) ;
238+ expect ( button ) . toBeDisabled ( ) ;
239+ expect ( button ) . toHaveValue ( "Creating rooms…" ) ; // Note the ellipsis
240+ } ) ;
241+ } ) ;
242+
243+ describe ( "Spaces: creating a new private space" , ( ) => {
244+ it ( "creates rooms inside a private space for a team" , async ( ) => {
245+ cli . createRoom . mockResolvedValueOnce ( { room_id : "room1" } ) . mockResolvedValueOnce ( { room_id : "room2" } ) ;
246+ SpaceStore . instance . addRoomToSpace = jest . fn ( ) ;
247+
248+ // When I create a private space
249+ const view = await renderSpaceRoomView ( {
250+ createOpts : { preset : Preset . PrivateChat } ,
251+ name : "Private space" ,
252+ topic : "a private space for team A" ,
253+ } ) ;
254+
255+ // Then I am asked whether it's individual or team
256+ expect ( view . getByRole ( "heading" , { name : "Who are you working with?" } ) ) . toBeInTheDocument ( ) ;
257+
258+ // And when I say team
259+ act ( ( ) =>
260+ view
261+ . getByRole ( "button" , {
262+ name : "Me and my teammates A private space for you and your teammates" ,
263+ } )
264+ . click ( ) ,
265+ ) ;
266+
267+ // Then I am asked what rooms to create
268+ expect ( view . getByRole ( "heading" , { name : "What projects are your team working on?" } ) ) . toBeInTheDocument ( ) ;
269+
270+ expect ( view . getByPlaceholderText ( / g e n e r a l / i) ) . toBeInTheDocument ( ) ;
271+ expect ( view . getByPlaceholderText ( / r a n d o m / i) ) . toBeInTheDocument ( ) ;
272+ expect ( view . getByPlaceholderText ( / s u p p o r t / i) ) . toBeInTheDocument ( ) ;
273+
274+ // And when I enter some room names
275+ const input1 = view . getAllByRole ( "textbox" ) [ 0 ] ;
276+ const input2 = view . getAllByRole ( "textbox" ) [ 1 ] ;
277+ fireEvent . change ( input1 , { target : { value : "Room 1" } } ) ;
278+ fireEvent . change ( input2 , { target : { value : "Room 2" } } ) ;
279+
280+ // And click "Continue"
281+ const button = view . getByRole ( "button" , { name : "Continue" } ) ;
282+ fireEvent . click ( button ) ;
283+
284+ // Then the rooms are created
285+ await waitFor ( ( ) => {
286+ expect ( cli . createRoom ) . toHaveBeenCalledTimes ( 2 ) ;
287+ } ) ;
288+ } ) ;
116289 } ) ;
117290} ) ;
0 commit comments