remove tests relying on scrollpanel behaviour
as BACAT scrolling relies on CSS from riot, which is not included in the karma tests, we're removing these tests in favor of later adding them to the e2e tests.
This commit is contained in:
parent
41c5582a7b
commit
30dc6a9150
2 changed files with 0 additions and 655 deletions
|
@ -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 (
|
|
||||||
<li key={key} data-scroll-tokens={key}>
|
|
||||||
<div style={{height: '98px', margin: '50px', border: '1px solid black',
|
|
||||||
backgroundColor: '#fff8dc' }}>
|
|
||||||
{ key }
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const tiles = this.state.tileKeys.map(this._mkTile);
|
|
||||||
console.log("rendering with " + tiles.length + " tiles");
|
|
||||||
return (
|
|
||||||
<ScrollPanel ref="sp"
|
|
||||||
onScroll={this._onScroll}
|
|
||||||
onFillRequest={this._onFillRequest}
|
|
||||||
resizeNotifier={this.state.resizeNotifier}>
|
|
||||||
{ tiles }
|
|
||||||
</ScrollPanel>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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(<Tester />, 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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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 <TimelinePanel ref="panel" {...this.props} />;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
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(
|
|
||||||
<WrappedTimelinePanel timelineSet={timelineSet} onScroll={onScroll} />,
|
|
||||||
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(
|
|
||||||
<WrappedTimelinePanel timelineSet={timelineSet} />,
|
|
||||||
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(
|
|
||||||
<WrappedTimelinePanel timelineSet={timelineSet} onScroll={() => {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);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in a new issue