359 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			359 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
/*
 | 
						|
Copyright 2018 New Vector Ltd.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
import React from 'react';
 | 
						|
import ReactDOM from 'react-dom';
 | 
						|
import ReactTestUtils from 'react-dom/test-utils';
 | 
						|
import expect from 'expect';
 | 
						|
 | 
						|
import MockHttpBackend from 'matrix-mock-request';
 | 
						|
import MatrixClientPeg from '../../../src/MatrixClientPeg';
 | 
						|
import sdk from 'matrix-react-sdk';
 | 
						|
import Matrix from 'matrix-js-sdk';
 | 
						|
 | 
						|
import * as TestUtils from 'test-utils';
 | 
						|
const { waitForUpdate } = TestUtils;
 | 
						|
 | 
						|
const GroupView = sdk.getComponent('structures.GroupView');
 | 
						|
const WrappedGroupView = TestUtils.wrapInMatrixClientContext(GroupView);
 | 
						|
 | 
						|
const Spinner = sdk.getComponent('elements.Spinner');
 | 
						|
 | 
						|
describe('GroupView', function() {
 | 
						|
    let root;
 | 
						|
    let rootElement;
 | 
						|
    let httpBackend;
 | 
						|
    let summaryResponse;
 | 
						|
    let summaryResponseWithComplicatedLongDesc;
 | 
						|
    let summaryResponseWithNoLongDesc;
 | 
						|
    let summaryResponseWithBadImg;
 | 
						|
    let groupId;
 | 
						|
    let groupIdEncoded;
 | 
						|
 | 
						|
    // Summary response fields
 | 
						|
    const user = {
 | 
						|
        is_privileged: true, // can edit the group
 | 
						|
        is_public: true, // appear as a member to non-members
 | 
						|
        is_publicised: true, // display flair
 | 
						|
    };
 | 
						|
    const usersSection = {
 | 
						|
        roles: {},
 | 
						|
        total_user_count_estimate: 0,
 | 
						|
        users: [],
 | 
						|
    };
 | 
						|
    const roomsSection = {
 | 
						|
        categories: {},
 | 
						|
        rooms: [],
 | 
						|
        total_room_count_estimate: 0,
 | 
						|
    };
 | 
						|
 | 
						|
    beforeEach(function() {
 | 
						|
        TestUtils.beforeEach(this);
 | 
						|
 | 
						|
        httpBackend = new MockHttpBackend();
 | 
						|
 | 
						|
        Matrix.request(httpBackend.requestFn);
 | 
						|
 | 
						|
        MatrixClientPeg.get = () => Matrix.createClient({
 | 
						|
            baseUrl: 'https://my.home.server',
 | 
						|
            userId: '@me:here',
 | 
						|
            accessToken: '123456789',
 | 
						|
        });
 | 
						|
 | 
						|
        summaryResponse = {
 | 
						|
            profile: {
 | 
						|
                avatar_url: "mxc://someavatarurl",
 | 
						|
                is_openly_joinable: true,
 | 
						|
                is_public: true,
 | 
						|
                long_description: "This is a <b>LONG</b> description.",
 | 
						|
                name: "The name of a community",
 | 
						|
                short_description: "This is a community",
 | 
						|
            },
 | 
						|
            user,
 | 
						|
            users_section: usersSection,
 | 
						|
            rooms_section: roomsSection,
 | 
						|
        };
 | 
						|
        summaryResponseWithNoLongDesc = {
 | 
						|
            profile: {
 | 
						|
                avatar_url: "mxc://someavatarurl",
 | 
						|
                is_openly_joinable: true,
 | 
						|
                is_public: true,
 | 
						|
                long_description: null,
 | 
						|
                name: "The name of a community",
 | 
						|
                short_description: "This is a community",
 | 
						|
            },
 | 
						|
            user,
 | 
						|
            users_section: usersSection,
 | 
						|
            rooms_section: roomsSection,
 | 
						|
        };
 | 
						|
        summaryResponseWithComplicatedLongDesc = {
 | 
						|
            profile: {
 | 
						|
                avatar_url: "mxc://someavatarurl",
 | 
						|
                is_openly_joinable: true,
 | 
						|
                is_public: true,
 | 
						|
                long_description: `
 | 
						|
<h1>This is a more complicated group page</h1>
 | 
						|
<p>With paragraphs</p>
 | 
						|
<ul>
 | 
						|
    <li>And lists!</li>
 | 
						|
    <li>With list items.</li>
 | 
						|
</ul>
 | 
						|
<p>And also images: <img src="mxc://someimageurl"/></p>`,
 | 
						|
                name: "The name of a community",
 | 
						|
                short_description: "This is a community",
 | 
						|
            },
 | 
						|
            user,
 | 
						|
            users_section: usersSection,
 | 
						|
            rooms_section: roomsSection,
 | 
						|
        };
 | 
						|
 | 
						|
        summaryResponseWithBadImg = {
 | 
						|
            profile: {
 | 
						|
                avatar_url: "mxc://someavatarurl",
 | 
						|
                is_openly_joinable: true,
 | 
						|
                is_public: true,
 | 
						|
                long_description: '<p>Evil image: <img src="http://evilimageurl"/></p>',
 | 
						|
                name: "The name of a community",
 | 
						|
                short_description: "This is a community",
 | 
						|
            },
 | 
						|
            user,
 | 
						|
            users_section: usersSection,
 | 
						|
            rooms_section: roomsSection,
 | 
						|
        };
 | 
						|
 | 
						|
        groupId = "+" + Math.random().toString(16).slice(2) + ':domain';
 | 
						|
        groupIdEncoded = encodeURIComponent(groupId);
 | 
						|
 | 
						|
        rootElement = document.createElement('div');
 | 
						|
        root = ReactDOM.render(<WrappedGroupView groupId={groupId} />, rootElement);
 | 
						|
    });
 | 
						|
 | 
						|
    afterEach(function() {
 | 
						|
        ReactDOM.unmountComponentAtNode(rootElement);
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a spinner when first displayed', function() {
 | 
						|
        ReactTestUtils.findRenderedComponentWithType(root, Spinner);
 | 
						|
 | 
						|
        // If we don't respond here, the rate limiting done to ensure a maximum of
 | 
						|
        // 3 concurrent network requests for GroupStore will block subsequent requests
 | 
						|
        // in other tests.
 | 
						|
        //
 | 
						|
        // This is a good case for doing the rate limiting somewhere other than the module
 | 
						|
        // scope of GroupStore.js
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        return httpBackend.flush(undefined, undefined, 0);
 | 
						|
    });
 | 
						|
 | 
						|
    it('should indicate failure after failed /summary', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_error');
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(500, {});
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a group avatar, name, id and short description after successful /summary', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView');
 | 
						|
 | 
						|
            const avatar = ReactTestUtils.findRenderedComponentWithType(root, sdk.getComponent('avatars.GroupAvatar'));
 | 
						|
            const img = ReactTestUtils.findRenderedDOMComponentWithTag(avatar, 'img');
 | 
						|
            const avatarImgElement = ReactDOM.findDOMNode(img);
 | 
						|
            expect(avatarImgElement).toExist();
 | 
						|
            expect(avatarImgElement.src).toInclude(
 | 
						|
                'https://my.home.server/_matrix/media/v1/thumbnail/' +
 | 
						|
                'someavatarurl?width=48&height=48&method=crop',
 | 
						|
            );
 | 
						|
 | 
						|
            const name = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_name');
 | 
						|
            const nameElement = ReactDOM.findDOMNode(name);
 | 
						|
            expect(nameElement).toExist();
 | 
						|
            expect(nameElement.innerText).toInclude('The name of a community');
 | 
						|
            expect(nameElement.innerText).toInclude(groupId);
 | 
						|
 | 
						|
            const shortDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_shortDesc');
 | 
						|
            const shortDescElement = ReactDOM.findDOMNode(shortDesc);
 | 
						|
            expect(shortDescElement).toExist();
 | 
						|
            expect(shortDescElement.innerText).toBe('This is a community');
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a simple long description after successful /summary', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView');
 | 
						|
 | 
						|
            const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
 | 
						|
            const longDescElement = ReactDOM.findDOMNode(longDesc);
 | 
						|
            expect(longDescElement).toExist();
 | 
						|
            expect(longDescElement.innerText).toBe('This is a LONG description.');
 | 
						|
            expect(longDescElement.innerHTML).toBe('<div dir="auto">This is a <b>LONG</b> description.</div>');
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a placeholder if a long description is not set', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            const placeholder = ReactTestUtils
 | 
						|
                .findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc_placeholder');
 | 
						|
            const placeholderElement = ReactDOM.findDOMNode(placeholder);
 | 
						|
            expect(placeholderElement).toExist();
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend
 | 
						|
            .when('GET', '/groups/' + groupIdEncoded + '/summary')
 | 
						|
            .respond(200, summaryResponseWithNoLongDesc);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a complicated long description after successful /summary', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
 | 
						|
            const longDescElement = ReactDOM.findDOMNode(longDesc);
 | 
						|
            expect(longDescElement).toExist();
 | 
						|
 | 
						|
            expect(longDescElement.innerHTML).toInclude('<h1>This is a more complicated group page</h1>');
 | 
						|
            expect(longDescElement.innerHTML).toInclude('<p>With paragraphs</p>');
 | 
						|
            expect(longDescElement.innerHTML).toInclude('<ul>');
 | 
						|
            expect(longDescElement.innerHTML).toInclude('<li>And lists!</li>');
 | 
						|
 | 
						|
            const imgSrc = "https://my.home.server/_matrix/media/v1/thumbnail/someimageurl?width=800&height=600";
 | 
						|
            expect(longDescElement.innerHTML).toInclude('<img src="' + imgSrc + '">');
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend
 | 
						|
            .when('GET', '/groups/' + groupIdEncoded + '/summary')
 | 
						|
            .respond(200, summaryResponseWithComplicatedLongDesc);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should disallow images with non-mxc URLs', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
 | 
						|
            const longDescElement = ReactDOM.findDOMNode(longDesc);
 | 
						|
            expect(longDescElement).toExist();
 | 
						|
 | 
						|
            // If this fails, the URL could be in an img `src`, which is what we care about but
 | 
						|
            // there's no harm in keeping this simple and checking the entire HTML string.
 | 
						|
            expect(longDescElement.innerHTML).toExclude('evilimageurl');
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend
 | 
						|
            .when('GET', '/groups/' + groupIdEncoded + '/summary')
 | 
						|
            .respond(200, summaryResponseWithBadImg);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a RoomDetailList after a successful /summary & /rooms (no rooms returned)', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            const roomDetailList = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_RoomDetailList');
 | 
						|
            const roomDetailListElement = ReactDOM.findDOMNode(roomDetailList);
 | 
						|
            expect(roomDetailListElement).toExist();
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
 | 
						|
    it('should show a RoomDetailList after a successful /summary & /rooms (with a single room)', function() {
 | 
						|
        const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
 | 
						|
        const prom = waitForUpdate(groupView).then(() => {
 | 
						|
            const roomDetailList = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_RoomDetailList');
 | 
						|
            const roomDetailListElement = ReactDOM.findDOMNode(roomDetailList);
 | 
						|
            expect(roomDetailListElement).toExist();
 | 
						|
 | 
						|
            const roomDetailListRoomName = ReactTestUtils.findRenderedDOMComponentWithClass(
 | 
						|
                root,
 | 
						|
                'mx_RoomDirectory_name',
 | 
						|
            );
 | 
						|
            const roomDetailListRoomNameElement = ReactDOM.findDOMNode(roomDetailListRoomName);
 | 
						|
 | 
						|
            expect(roomDetailListRoomNameElement).toExist();
 | 
						|
            expect(roomDetailListRoomNameElement.innerText).toEqual('Some room name');
 | 
						|
        });
 | 
						|
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
 | 
						|
        httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [{
 | 
						|
            avatar_url: "mxc://someroomavatarurl",
 | 
						|
            canonical_alias: "#somealias:domain",
 | 
						|
            guest_can_join: true,
 | 
						|
            is_public: true,
 | 
						|
            name: "Some room name",
 | 
						|
            num_joined_members: 123,
 | 
						|
            room_id: "!someroomid",
 | 
						|
            topic: "some topic",
 | 
						|
            world_readable: true,
 | 
						|
        }] });
 | 
						|
 | 
						|
        httpBackend.flush(undefined, undefined, 0);
 | 
						|
        return prom;
 | 
						|
    });
 | 
						|
});
 |