diff --git a/test/components/structures/ScrollPanel-test.js b/test/components/structures/ScrollPanel-test.js
deleted file mode 100644
index 41d0f4469b..0000000000
--- a/test/components/structures/ScrollPanel-test.js
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
-Copyright 2016 OpenMarket 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.
-*/
-
-const React = require('react');
-const ReactDOM = require("react-dom");
-const ReactTestUtils = require('react-addons-test-utils');
-const expect = require('expect');
-import Promise from 'bluebird';
-import { EventEmitter } from "events";
-
-const sdk = require('matrix-react-sdk');
-
-const ScrollPanel = sdk.getComponent('structures.ScrollPanel');
-const test_utils = require('test-utils');
-
-const Tester = React.createClass({
- getInitialState: function() {
- return {
- tileKeys: [],
- resizeNotifier: new EventEmitter(),
- };
- },
-
- componentWillMount: function() {
- this.fillCounts = {'b': 0, 'f': 0};
- this._fillHandlers = {'b': null, 'f': null};
- this._fillDefers = {'b': null, 'f': null};
- this._scrollDefer = null;
-
- // scrollTop at the last scroll event
- this.lastScrollEvent = null;
- },
-
- _onFillRequest: function(back) {
- const dir = back ? 'b': 'f';
- console.log("FillRequest: " + dir);
- this.fillCounts[dir]++;
-
- const handler = this._fillHandlers[dir];
- const defer = this._fillDefers[dir];
-
- // don't use the same handler twice
- this._fillHandlers[dir] = null;
- this._fillDefers[dir] = null;
-
- let res;
- if (handler) {
- res = handler();
- } else {
- res = Promise.resolve(false);
- }
-
- if (defer) {
- defer.resolve();
- }
- return res;
- },
-
- addFillHandler: function(dir, handler) {
- this._fillHandlers[dir] = handler;
- },
-
- /* returns a promise which will resolve when the fill happens */
- awaitFill: function(dir) {
- console.log("ScrollPanel Tester: awaiting " + dir + " fill");
- const defer = Promise.defer();
- this._fillDefers[dir] = defer;
- return defer.promise;
- },
-
- _onScroll: function(ev) {
- const st = ev.target.scrollTop;
- console.log("ScrollPanel Tester: scroll event; scrollTop: " + st);
- this.lastScrollEvent = st;
-
- const d = this._scrollDefer;
- if (d) {
- this._scrollDefer = null;
- d.resolve();
- }
- },
-
- /* returns a promise which will resolve when a scroll event happens */
- awaitScroll: function() {
- console.log("Awaiting scroll");
- this._scrollDefer = Promise.defer();
- return this._scrollDefer.promise;
- },
-
- setTileKeys: function(keys) {
- console.log("Updating keys: len=" + keys.length);
- this.setState({tileKeys: keys.slice()});
- },
-
- scrollPanel: function() {
- return this.refs.sp;
- },
-
- _mkTile: function(key) {
- // each tile is 150 pixels high:
- // 98 pixels of body
- // 2 pixels of border
- // 50 pixels of margin
- //
- // there is an extra 50 pixels of margin at the bottom.
- return (
-
-
- { key }
-
-
- );
- },
-
- render: function() {
- const tiles = this.state.tileKeys.map(this._mkTile);
- console.log("rendering with " + tiles.length + " tiles");
- return (
-
- { tiles }
-
- );
- },
-});
-
-describe('ScrollPanel', function() {
- let parentDiv;
- let tester;
- let scrollingDiv;
-
- beforeEach(function(done) {
- test_utils.beforeEach(this);
-
- // create a div of a useful size to put our panel in, and attach it to
- // the document so that we can interact with it properly.
- parentDiv = document.createElement('div');
- parentDiv.style.width = '800px';
- parentDiv.style.height = '600px';
- parentDiv.style.overflow = 'hidden';
- document.body.appendChild(parentDiv);
-
- tester = ReactDOM.render(, parentDiv);
- expect(tester.fillCounts.b).toEqual(1);
- expect(tester.fillCounts.f).toEqual(1);
-
- scrollingDiv = ReactTestUtils.findRenderedDOMComponentWithClass(
- tester, "gm-scroll-view");
-
- // we need to make sure we don't call done() until q has finished
- // running the completion handlers from the fill requests. We can't
- // just use .done(), because that will end up ahead of those handlers
- // in the queue. We can't use window.setTimeout(0), because that also might
- // run ahead of those handlers.
- const sp = tester.scrollPanel();
- let retriesRemaining = 1;
- const awaitReady = function() {
- return Promise.resolve().then(() => {
- if (sp._pendingFillRequests.b === false &&
- sp._pendingFillRequests.f === false
- ) {
- return;
- }
-
- if (retriesRemaining == 0) {
- throw new Error("fillRequests did not complete");
- }
- retriesRemaining--;
- return awaitReady();
- });
- };
- awaitReady().done(done);
- });
-
- afterEach(function() {
- if (parentDiv) {
- document.body.removeChild(parentDiv);
- parentDiv = null;
- }
- });
-
- it('should handle scrollEvent strangeness', function() {
- const events = [];
-
- return Promise.resolve().then(() => {
- // initialise with a load of events
- for (let i = 0; i < 20; i++) {
- events.push(i+80);
- }
- tester.setTileKeys(events);
- expect(scrollingDiv.scrollHeight).toEqual(3050); // 20*150 + 50
- expect(scrollingDiv.scrollTop).toEqual(3050 - 600);
- return tester.awaitScroll();
- }).then(() => {
- expect(tester.lastScrollEvent).toBe(3050 - 600);
-
- tester.scrollPanel().scrollToToken("92", 0);
-
- // at this point, ScrollPanel will have updated scrollTop, but
- // the event hasn't fired.
- expect(tester.lastScrollEvent).toEqual(3050 - 600);
- expect(scrollingDiv.scrollTop).toEqual(1950);
-
- // now stamp over the scrollTop.
- console.log('faking #528');
- scrollingDiv.scrollTop = 500;
-
- return tester.awaitScroll();
- }).then(() => {
- expect(tester.lastScrollEvent).toBe(1950);
- expect(scrollingDiv.scrollTop).toEqual(1950);
- });
- });
-
- it('should not get stuck in #528 workaround', function(done) {
- let events = [];
- Promise.resolve().then(() => {
- // initialise with a bunch of events
- for (let i = 0; i < 40; i++) {
- events.push(i);
- }
- tester.setTileKeys(events);
- expect(tester.fillCounts.b).toEqual(1);
- expect(tester.fillCounts.f).toEqual(2);
- expect(scrollingDiv.scrollHeight).toEqual(6050); // 40*150 + 50
- expect(scrollingDiv.scrollTop).toEqual(6050 - 600);
-
- // try to scroll up, to a non-integer offset.
- tester.scrollPanel().scrollToToken("30", -101/3);
-
- expect(scrollingDiv.scrollTop).toEqual(4616); // 31*150 - 34
-
- // wait for the scroll event to land
- return tester.awaitScroll(); // fails
- }).then(() => {
- expect(tester.lastScrollEvent).toEqual(4616);
-
- // Now one more event; this will make it reset the scroll, but
- // because the delta will be less than 1, will not trigger a
- // scroll event, this leaving recentEventScroll defined.
- console.log("Adding event 50");
- events.push(50);
- tester.setTileKeys(events);
-
- // wait for the scrollpanel to stop trying to paginate
- }).then(() => {
- // Now, simulate hitting "scroll to bottom".
- events = [];
- for (let i = 100; i < 120; i++) {
- events.push(i);
- }
- tester.setTileKeys(events);
- tester.scrollPanel().scrollToBottom();
-
- // wait for the scroll event to land
- return tester.awaitScroll(); // fails
- }).then(() => {
- expect(scrollingDiv.scrollTop).toEqual(20*150 + 50 - 600);
-
- // simulate a user-initiated scroll on the div
- scrollingDiv.scrollTop = 1200;
- return tester.awaitScroll();
- }).then(() => {
- expect(scrollingDiv.scrollTop).toEqual(1200);
- }).done(done);
- });
-});
diff --git a/test/components/structures/TimelinePanel-test.js b/test/components/structures/TimelinePanel-test.js
deleted file mode 100644
index 01ea6d8421..0000000000
--- a/test/components/structures/TimelinePanel-test.js
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
-Copyright 2016 OpenMarket 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.
-*/
-
-const React = require('react');
-const ReactDOM = require('react-dom');
-const ReactTestUtils = require('react-addons-test-utils');
-const expect = require('expect');
-import Promise from 'bluebird';
-const sinon = require('sinon');
-
-const jssdk = require('matrix-js-sdk');
-const EventTimeline = jssdk.EventTimeline;
-
-const sdk = require('matrix-react-sdk');
-const TimelinePanel = sdk.getComponent('structures.TimelinePanel');
-const peg = require('../../../src/MatrixClientPeg');
-
-const test_utils = require('test-utils');
-
-const ROOM_ID = '!room:localhost';
-const USER_ID = '@me:localhost';
-
-// wrap TimelinePanel with a component which provides the MatrixClient in the context.
-const WrappedTimelinePanel = React.createClass({
- childContextTypes: {
- matrixClient: React.PropTypes.object,
- },
-
- getChildContext: function() {
- return {
- matrixClient: peg.get(),
- };
- },
-
- render: function() {
- return ;
- },
-});
-
-
-describe('TimelinePanel', function() {
- let sandbox;
- let timelineSet;
- let room;
- let client;
- let timeline;
- let parentDiv;
-
- // make a dummy message. eventNum is put in the message text to help
- // identification during debugging, and also in the timestamp so that we
- // don't get lots of events with the same timestamp.
- function mkMessage(eventNum, opts) {
- return test_utils.mkMessage(
- {
- event: true, room: ROOM_ID, user: USER_ID,
- ts: Date.now() + eventNum,
- msg: "Event " + eventNum,
- ...opts,
- });
- }
-
- function scryEventTiles(panel) {
- return ReactTestUtils.scryRenderedComponentsWithType(
- panel, sdk.getComponent('rooms.EventTile'));
- }
-
- beforeEach(function() {
- test_utils.beforeEach(this);
- sandbox = test_utils.stubClient(sandbox);
-
- room = sinon.createStubInstance(jssdk.Room);
- room.currentState = sinon.createStubInstance(jssdk.RoomState);
- room.currentState.members = {};
- room.roomId = ROOM_ID;
-
- timelineSet = sinon.createStubInstance(jssdk.EventTimelineSet);
- timelineSet.getPendingEvents.returns([]);
- timelineSet.room = room;
-
- timeline = new jssdk.EventTimeline(timelineSet);
-
- timelineSet.getLiveTimeline.returns(timeline);
-
- client = peg.get();
- client.credentials = {userId: USER_ID};
-
- // create a div of a useful size to put our panel in, and attach it to
- // the document so that we can interact with it properly.
- parentDiv = document.createElement('div');
- parentDiv.style.width = '800px';
-
- // This has to be slightly carefully chosen. We expect to have to do
- // exactly one pagination to fill it.
- parentDiv.style.height = '500px';
-
- parentDiv.style.overflow = 'hidden';
- document.body.appendChild(parentDiv);
- });
-
- afterEach(function() {
- if (parentDiv) {
- ReactDOM.unmountComponentAtNode(parentDiv);
- parentDiv.remove();
- parentDiv = null;
- }
- sandbox.restore();
- });
-
- it('should load new events even if you are scrolled up', function(done) {
- // this is https://github.com/vector-im/vector-web/issues/1367
-
- // enough events to allow us to scroll back
- const N_EVENTS = 30;
- for (let i = 0; i < N_EVENTS; i++) {
- timeline.addEvent(mkMessage(i));
- }
-
- let scrollDefer;
- const onScroll = (e) => {
- console.log(`TimelinePanel called onScroll: ${e.target.scrollTop}`);
- if (scrollDefer) {
- scrollDefer.resolve();
- }
- };
- const rendered = ReactDOM.render(
- ,
- parentDiv,
- );
- const panel = rendered.refs.panel;
- const scrollingDiv = ReactTestUtils.findRenderedDOMComponentWithClass(
- panel, "gm-scroll-view");
-
- // helper function which will return a promise which resolves when the
- // panel isn't paginating
- var awaitPaginationCompletion = function() {
- if(!panel.state.forwardPaginating) {return Promise.resolve();} else {return Promise.delay(0).then(awaitPaginationCompletion);}
- };
-
- // helper function which will return a promise which resolves when
- // the TimelinePanel fires a scroll event
- const awaitScroll = function() {
- scrollDefer = Promise.defer();
- return scrollDefer.promise;
- };
-
- // let the first round of pagination finish off
- Promise.delay(5).then(() => {
- expect(panel.state.canBackPaginate).toBe(false);
- expect(scryEventTiles(panel).length).toEqual(N_EVENTS);
-
- // scroll up
- console.log("setting scrollTop = 0");
- scrollingDiv.scrollTop = 0;
-
- // wait for the scroll event to land
- }).then(awaitScroll).then(() => {
- expect(scrollingDiv.scrollTop).toEqual(0);
-
- // there should be no pagination going on now
- expect(panel.state.backPaginating).toBe(false);
- expect(panel.state.forwardPaginating).toBe(false);
- expect(panel.state.canBackPaginate).toBe(false);
- expect(panel.state.canForwardPaginate).toBe(false);
- expect(panel.isAtEndOfLiveTimeline()).toBe(false);
- expect(scrollingDiv.scrollTop).toEqual(0);
-
- console.log("adding event");
-
- // a new event!
- const ev = mkMessage(N_EVENTS+1);
- timeline.addEvent(ev);
- panel.onRoomTimeline(ev, room, false, false, {
- liveEvent: true,
- timeline: timeline,
- });
-
- // that won't make much difference, because we don't paginate
- // unless we're at the bottom of the timeline, but a scroll event
- // should be enough to set off a pagination.
- expect(scryEventTiles(panel).length).toEqual(N_EVENTS);
-
- scrollingDiv.scrollTop = 10;
-
- return awaitScroll();
- }).then(awaitPaginationCompletion).then(() => {
- expect(scryEventTiles(panel).length).toEqual(N_EVENTS+1);
- }).done(done, done);
- });
-
- it('should not paginate forever if there are no events', function(done) {
- // start with a handful of events in the timeline, as would happen when
- // joining a room
- const d = Date.now();
- for (let i = 0; i < 3; i++) {
- timeline.addEvent(mkMessage(i));
- }
- timeline.setPaginationToken('tok', EventTimeline.BACKWARDS);
-
- // back-pagination returns a promise for true, but adds no events
- client.paginateEventTimeline = sinon.spy((tl, opts) => {
- console.log("paginate:", opts);
- expect(opts.backwards).toBe(true);
- return Promise.resolve(true);
- });
-
- const rendered = ReactDOM.render(
- ,
- parentDiv,
- );
- const panel = rendered.refs.panel;
-
- const messagePanel = ReactTestUtils.findRenderedComponentWithType(
- panel, sdk.getComponent('structures.MessagePanel'));
-
- expect(messagePanel.props.backPaginating).toBe(true);
-
- // let the first round of pagination finish off
- setTimeout(() => {
- // at this point, the timeline window should have tried to paginate
- // 5 times, and we should have given up paginating
- expect(client.paginateEventTimeline.callCount).toEqual(5);
- expect(messagePanel.props.backPaginating).toBe(false);
- expect(messagePanel.props.suppressFirstDateSeparator).toBe(false);
-
- // now, if we update the events, there shouldn't be any
- // more requests.
- client.paginateEventTimeline.resetHistory();
- panel.forceUpdate();
- expect(messagePanel.props.backPaginating).toBe(false);
- setTimeout(() => {
- expect(client.paginateEventTimeline.callCount).toEqual(0);
- done();
- }, 0);
- }, 10);
- });
-
- it("should let you scroll down to the bottom after you've scrolled up", function(done) {
- const N_EVENTS = 120; // the number of events to simulate being added to the timeline
-
- // sadly, loading all those events takes a while
- this.timeout(N_EVENTS * 50);
-
- // client.getRoom is called a /lot/ in this test, so replace
- // sinon's spy with a fast noop.
- client.getRoom = function(id) { return null; };
-
- // fill the timeline with lots of events
- for (let i = 0; i < N_EVENTS; i++) {
- timeline.addEvent(mkMessage(i));
- }
- console.log("added events to timeline");
-
- let scrollDefer;
- const rendered = ReactDOM.render(
- {scrollDefer.resolve();}} />,
- parentDiv,
- );
- console.log("TimelinePanel rendered");
- const panel = rendered.refs.panel;
- const messagePanel = ReactTestUtils.findRenderedComponentWithType(
- panel, sdk.getComponent('structures.MessagePanel'));
- const scrollingDiv = ReactTestUtils.findRenderedDOMComponentWithClass(
- panel, "gm-scroll-view");
-
- // helper function which will return a promise which resolves when
- // the TimelinePanel fires a scroll event
- const awaitScroll = function() {
- scrollDefer = Promise.defer();
-
- return scrollDefer.promise.then(() => {
- console.log("got scroll event; scrollTop now " +
- scrollingDiv.scrollTop);
- });
- };
-
- function setScrollTop(scrollTop) {
- const before = scrollingDiv.scrollTop;
- scrollingDiv.scrollTop = scrollTop;
- console.log("setScrollTop: before update: " + before +
- "; assigned: " + scrollTop +
- "; after update: " + scrollingDiv.scrollTop);
- }
-
- function backPaginate() {
- console.log("back paginating...");
- setScrollTop(0);
- return awaitScroll().then(() => {
- const eventTiles = scryEventTiles(panel);
- const firstEvent = eventTiles[0].props.mxEvent;
-
- console.log("TimelinePanel contains " + eventTiles.length +
- " events; first is " +
- firstEvent.getContent().body);
-
- if(scrollingDiv.scrollTop > 0) {
- // need to go further
- return backPaginate();
- }
- console.log("paginated to start.");
- });
- }
-
- function scrollDown() {
- // Scroll the bottom of the viewport to the bottom of the panel
- setScrollTop(scrollingDiv.scrollHeight - scrollingDiv.clientHeight);
- console.log("scrolling down... " + scrollingDiv.scrollTop);
- return awaitScroll().delay(0).then(() => {
- const eventTiles = scryEventTiles(panel);
- const events = timeline.getEvents();
-
- const lastEventInPanel = eventTiles[eventTiles.length - 1].props.mxEvent;
- const lastEventInTimeline = events[events.length - 1];
-
- // Scroll until the last event in the panel = the last event in the timeline
- if(lastEventInPanel.getId() !== lastEventInTimeline.getId()) {
- // need to go further
- return scrollDown();
- }
- console.log("paginated to end.");
- });
- }
-
- // let the first round of pagination finish off
- awaitScroll().then(() => {
- // we should now have loaded the first few events
- expect(messagePanel.props.backPaginating).toBe(false);
- expect(messagePanel.props.suppressFirstDateSeparator).toBe(true);
-
- // back-paginate until we hit the start
- return backPaginate();
- }).then(() => {
- // hopefully, we got to the start of the timeline
- expect(messagePanel.props.backPaginating).toBe(false);
-
- expect(messagePanel.props.suppressFirstDateSeparator).toBe(false);
- const events = scryEventTiles(panel);
- expect(events[0].props.mxEvent).toBe(timeline.getEvents()[0]);
-
- // At this point, we make no assumption that unpagination has happened. This doesn't
- // mean that we shouldn't be able to scroll all the way down to the bottom to see the
- // most recent event in the timeline.
-
- // scroll all the way to the bottom
- return scrollDown();
- }).then(() => {
- expect(messagePanel.props.backPaginating).toBe(false);
- expect(messagePanel.props.forwardPaginating).toBe(false);
-
- const events = scryEventTiles(panel);
-
- // Expect to be able to see the most recent event
- const lastEventInPanel = events[events.length - 1].props.mxEvent;
- const lastEventInTimeline = timeline.getEvents()[timeline.getEvents().length - 1];
- expect(lastEventInPanel.getContent()).toBe(lastEventInTimeline.getContent());
-
- console.log("done");
- }).done(done, done);
- });
-});