Merge pull request #6304 from matrix-org/dbkr/tests_lint
Fix Test Linting
This commit is contained in:
commit
bd470f8897
6 changed files with 29 additions and 487 deletions
|
@ -1,15 +0,0 @@
|
||||||
# autogenerated file: run scripts/generate-eslint-error-ignore-file to update.
|
|
||||||
|
|
||||||
src/Markdown.js
|
|
||||||
src/NodeAnimator.js
|
|
||||||
src/components/structures/RoomDirectory.js
|
|
||||||
src/components/views/rooms/MemberList.js
|
|
||||||
src/utils/DMRoomMap.js
|
|
||||||
src/utils/MultiInviter.js
|
|
||||||
test/components/structures/MessagePanel-test.js
|
|
||||||
test/components/views/dialogs/InteractiveAuthDialog-test.js
|
|
||||||
test/mock-clock.js
|
|
||||||
src/component-index.js
|
|
||||||
test/end-to-end-tests/node_modules/
|
|
||||||
test/end-to-end-tests/element/
|
|
||||||
test/end-to-end-tests/synapse/
|
|
|
@ -45,7 +45,7 @@
|
||||||
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"",
|
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"",
|
||||||
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
||||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
|
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
|
||||||
"lint:js": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
|
"lint:js": "eslint --max-warnings 0 src test",
|
||||||
"lint:types": "tsc --noEmit --jsx react",
|
"lint:types": "tsc --noEmit --jsx react",
|
||||||
"lint:style": "stylelint 'res/css/**/*.scss'",
|
"lint:style": "stylelint 'res/css/**/*.scss'",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# generates .eslintignore.errorfiles to list the files which have errors in,
|
|
||||||
# so that they can be ignored in future automated linting.
|
|
||||||
|
|
||||||
out=.eslintignore.errorfiles
|
|
||||||
|
|
||||||
cd `dirname $0`/..
|
|
||||||
|
|
||||||
echo "generating $out"
|
|
||||||
|
|
||||||
{
|
|
||||||
cat <<EOF
|
|
||||||
# autogenerated file: run scripts/generate-eslint-error-ignore-file to update.
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
./node_modules/.bin/eslint -f json src test |
|
|
||||||
jq -r '.[] | select((.errorCount + .warningCount) > 0) | .filePath' |
|
|
||||||
sed -e 's/.*matrix-react-sdk\///';
|
|
||||||
} > "$out"
|
|
||||||
# also append rules from eslintignore file
|
|
||||||
cat .eslintignore >> $out
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -26,11 +26,11 @@ import { EventEmitter } from "events";
|
||||||
import sdk from '../../skinned-sdk';
|
import sdk from '../../skinned-sdk';
|
||||||
|
|
||||||
const MessagePanel = sdk.getComponent('structures.MessagePanel');
|
const MessagePanel = sdk.getComponent('structures.MessagePanel');
|
||||||
import {MatrixClientPeg} from '../../../src/MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../src/MatrixClientPeg';
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
|
|
||||||
const test_utils = require('../../test-utils');
|
const TestUtilsMatrix = require('../../test-utils');
|
||||||
const mockclock = require('../../mock-clock');
|
import FakeTimers from '@sinonjs/fake-timers';
|
||||||
|
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { configure, mount } from "enzyme";
|
import { configure, mount } from "enzyme";
|
||||||
|
@ -72,14 +72,14 @@ class WrappedMessagePanel extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('MessagePanel', function() {
|
describe('MessagePanel', function() {
|
||||||
const clock = mockclock.clock();
|
let clock = null;
|
||||||
const realSetTimeout = window.setTimeout;
|
const realSetTimeout = window.setTimeout;
|
||||||
const events = mkEvents();
|
const events = mkEvents();
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
test_utils.stubClient();
|
TestUtilsMatrix.stubClient();
|
||||||
client = MatrixClientPeg.get();
|
client = MatrixClientPeg.get();
|
||||||
client.credentials = {userId: '@me:here'};
|
client.credentials = { userId: '@me:here' };
|
||||||
|
|
||||||
// HACK: We assume all settings want to be disabled
|
// HACK: We assume all settings want to be disabled
|
||||||
SettingsStore.getValue = jest.fn((arg) => {
|
SettingsStore.getValue = jest.fn((arg) => {
|
||||||
|
@ -90,14 +90,17 @@ describe('MessagePanel', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
clock.uninstall();
|
if (clock) {
|
||||||
|
clock.uninstall();
|
||||||
|
clock = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function mkEvents() {
|
function mkEvents() {
|
||||||
const events = [];
|
const events = [];
|
||||||
const ts0 = Date.now();
|
const ts0 = Date.now();
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
events.push(test_utils.mkMessage(
|
events.push(TestUtilsMatrix.mkMessage(
|
||||||
{
|
{
|
||||||
event: true, room: "!room:id", user: "@user:id",
|
event: true, room: "!room:id", user: "@user:id",
|
||||||
ts: ts0 + i * 1000,
|
ts: ts0 + i * 1000,
|
||||||
|
@ -111,7 +114,7 @@ describe('MessagePanel', function() {
|
||||||
const events = [];
|
const events = [];
|
||||||
const ts0 = Date.parse('09 May 2004 00:12:00 GMT');
|
const ts0 = Date.parse('09 May 2004 00:12:00 GMT');
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
events.push(test_utils.mkMessage(
|
events.push(TestUtilsMatrix.mkMessage(
|
||||||
{
|
{
|
||||||
event: true, room: "!room:id", user: "@user:id",
|
event: true, room: "!room:id", user: "@user:id",
|
||||||
ts: ts0 + i * 1000,
|
ts: ts0 + i * 1000,
|
||||||
|
@ -120,7 +123,6 @@ describe('MessagePanel', function() {
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// make a collection of events with some member events that should be collapsed
|
// make a collection of events with some member events that should be collapsed
|
||||||
// with a MemberEventListSummary
|
// with a MemberEventListSummary
|
||||||
function mkMelsEvents() {
|
function mkMelsEvents() {
|
||||||
|
@ -128,13 +130,13 @@ describe('MessagePanel', function() {
|
||||||
const ts0 = Date.now();
|
const ts0 = Date.now();
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
events.push(test_utils.mkMessage({
|
events.push(TestUtilsMatrix.mkMessage({
|
||||||
event: true, room: "!room:id", user: "@user:id",
|
event: true, room: "!room:id", user: "@user:id",
|
||||||
ts: ts0 + ++i * 1000,
|
ts: ts0 + ++i * 1000,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
events.push(test_utils.mkMembership({
|
events.push(TestUtilsMatrix.mkMembership({
|
||||||
event: true, room: "!room:id", user: "@user:id",
|
event: true, room: "!room:id", user: "@user:id",
|
||||||
target: {
|
target: {
|
||||||
userId: "@user:id",
|
userId: "@user:id",
|
||||||
|
@ -151,7 +153,7 @@ describe('MessagePanel', function() {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
events.push(test_utils.mkMessage({
|
events.push(TestUtilsMatrix.mkMessage({
|
||||||
event: true, room: "!room:id", user: "@user:id",
|
event: true, room: "!room:id", user: "@user:id",
|
||||||
ts: ts0 + ++i*1000,
|
ts: ts0 + ++i*1000,
|
||||||
}));
|
}));
|
||||||
|
@ -167,7 +169,7 @@ describe('MessagePanel', function() {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
events.push(test_utils.mkMembership({
|
events.push(TestUtilsMatrix.mkMembership({
|
||||||
event: true, room: "!room:id", user: "@user:id",
|
event: true, room: "!room:id", user: "@user:id",
|
||||||
target: {
|
target: {
|
||||||
userId: "@user:id",
|
userId: "@user:id",
|
||||||
|
@ -189,8 +191,8 @@ describe('MessagePanel', function() {
|
||||||
|
|
||||||
// A list of room creation, encryption, and invite events.
|
// A list of room creation, encryption, and invite events.
|
||||||
function mkCreationEvents() {
|
function mkCreationEvents() {
|
||||||
const mkEvent = test_utils.mkEvent;
|
const mkEvent = TestUtilsMatrix.mkEvent;
|
||||||
const mkMembership = test_utils.mkMembership;
|
const mkMembership = TestUtilsMatrix.mkMembership;
|
||||||
const roomId = "!someroom";
|
const roomId = "!someroom";
|
||||||
const alice = "@alice:example.org";
|
const alice = "@alice:example.org";
|
||||||
const ts0 = Date.now();
|
const ts0 = Date.now();
|
||||||
|
@ -363,8 +365,7 @@ describe('MessagePanel', function() {
|
||||||
|
|
||||||
it('shows a ghost read-marker when the read-marker moves', function(done) {
|
it('shows a ghost read-marker when the read-marker moves', function(done) {
|
||||||
// fake the clock so that we can test the velocity animation.
|
// fake the clock so that we can test the velocity animation.
|
||||||
clock.install();
|
clock = FakeTimers.install();
|
||||||
clock.mockDate();
|
|
||||||
|
|
||||||
const parentDiv = document.createElement('div');
|
const parentDiv = document.createElement('div');
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ import ReactTestUtils from 'react-dom/test-utils';
|
||||||
import MatrixReactTestUtils from 'matrix-react-test-utils';
|
import MatrixReactTestUtils from 'matrix-react-test-utils';
|
||||||
|
|
||||||
import sdk from '../../../skinned-sdk';
|
import sdk from '../../../skinned-sdk';
|
||||||
import {MatrixClientPeg} from '../../../../src/MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||||
|
|
||||||
import * as test_utils from '../../../test-utils';
|
import * as TestUtilsMatrix from '../../../test-utils';
|
||||||
import {sleep} from "../../../../src/utils/promise";
|
import { sleep } from "../../../../src/utils/promise";
|
||||||
|
|
||||||
const InteractiveAuthDialog = sdk.getComponent(
|
const InteractiveAuthDialog = sdk.getComponent(
|
||||||
'views.dialogs.InteractiveAuthDialog',
|
'views.dialogs.InteractiveAuthDialog',
|
||||||
|
@ -33,7 +33,7 @@ describe('InteractiveAuthDialog', function() {
|
||||||
let parentDiv;
|
let parentDiv;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
test_utils.stubClient();
|
TestUtilsMatrix.stubClient();
|
||||||
parentDiv = document.createElement('div');
|
parentDiv = document.createElement('div');
|
||||||
document.body.appendChild(parentDiv);
|
document.body.appendChild(parentDiv);
|
||||||
});
|
});
|
||||||
|
@ -45,11 +45,11 @@ describe('InteractiveAuthDialog', function() {
|
||||||
|
|
||||||
it('Should successfully complete a password flow', function() {
|
it('Should successfully complete a password flow', function() {
|
||||||
const onFinished = jest.fn();
|
const onFinished = jest.fn();
|
||||||
const doRequest = jest.fn().mockResolvedValue({a: 1});
|
const doRequest = jest.fn().mockResolvedValue({ a: 1 });
|
||||||
|
|
||||||
// tell the stub matrixclient to return a real userid
|
// tell the stub matrixclient to return a real userid
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
client.credentials = {userId: "@user:id"};
|
client.credentials = { userId: "@user:id" };
|
||||||
|
|
||||||
const dlg = ReactDOM.render(
|
const dlg = ReactDOM.render(
|
||||||
<InteractiveAuthDialog
|
<InteractiveAuthDialog
|
||||||
|
@ -57,7 +57,7 @@ describe('InteractiveAuthDialog', function() {
|
||||||
authData={{
|
authData={{
|
||||||
session: "sess",
|
session: "sess",
|
||||||
flows: [
|
flows: [
|
||||||
{"stages": ["m.login.password"]},
|
{ "stages": ["m.login.password"] },
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
makeRequest={doRequest}
|
makeRequest={doRequest}
|
||||||
|
@ -105,7 +105,7 @@ describe('InteractiveAuthDialog', function() {
|
||||||
return sleep(1);
|
return sleep(1);
|
||||||
}).then(sleep(1)).then(() => {
|
}).then(sleep(1)).then(() => {
|
||||||
expect(onFinished).toBeCalledTimes(1);
|
expect(onFinished).toBeCalledTimes(1);
|
||||||
expect(onFinished).toBeCalledWith(true, {a: 1});
|
expect(onFinished).toBeCalledWith(true, { a: 1 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,421 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (c) 2008-2015 Pivotal Labs
|
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This is jasmine's implementation of a mock clock, lifted from the depths of
|
|
||||||
* jasmine-core and exposed as a standalone module. The interface is just the
|
|
||||||
* same as that of jasmine.clock. For example:
|
|
||||||
*
|
|
||||||
* var mock_clock = require("../../mock-clock").clock();
|
|
||||||
* mock_clock.install();
|
|
||||||
* setTimeout(function() {
|
|
||||||
* timerCallback();
|
|
||||||
* }, 100);
|
|
||||||
*
|
|
||||||
* expect(timerCallback).not.toHaveBeenCalled();
|
|
||||||
* mock_clock.tick(101);
|
|
||||||
* expect(timerCallback).toHaveBeenCalled();
|
|
||||||
*
|
|
||||||
* mock_clock.uninstall();
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The reason for C&Ping jasmine's clock here is that jasmine itself is
|
|
||||||
* difficult to webpack, and we don't really want all of it. Sinon also has a
|
|
||||||
* mock-clock implementation, but again, it is difficult to webpack.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const j$ = {};
|
|
||||||
|
|
||||||
j$.Clock = function() {
|
|
||||||
function Clock(global, delayedFunctionSchedulerFactory, mockDate) {
|
|
||||||
let self = this,
|
|
||||||
realTimingFunctions = {
|
|
||||||
setTimeout: global.setTimeout,
|
|
||||||
clearTimeout: global.clearTimeout,
|
|
||||||
setInterval: global.setInterval,
|
|
||||||
clearInterval: global.clearInterval,
|
|
||||||
},
|
|
||||||
fakeTimingFunctions = {
|
|
||||||
setTimeout: setTimeout,
|
|
||||||
clearTimeout: clearTimeout,
|
|
||||||
setInterval: setInterval,
|
|
||||||
clearInterval: clearInterval,
|
|
||||||
},
|
|
||||||
installed = false,
|
|
||||||
delayedFunctionScheduler,
|
|
||||||
timer;
|
|
||||||
|
|
||||||
|
|
||||||
self.install = function() {
|
|
||||||
if(!originalTimingFunctionsIntact()) {
|
|
||||||
throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?');
|
|
||||||
}
|
|
||||||
replace(global, fakeTimingFunctions);
|
|
||||||
timer = fakeTimingFunctions;
|
|
||||||
delayedFunctionScheduler = delayedFunctionSchedulerFactory();
|
|
||||||
installed = true;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.uninstall = function() {
|
|
||||||
delayedFunctionScheduler = null;
|
|
||||||
mockDate.uninstall();
|
|
||||||
replace(global, realTimingFunctions);
|
|
||||||
|
|
||||||
timer = realTimingFunctions;
|
|
||||||
installed = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.withMock = function(closure) {
|
|
||||||
this.install();
|
|
||||||
try {
|
|
||||||
closure();
|
|
||||||
} finally {
|
|
||||||
this.uninstall();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.mockDate = function(initialDate) {
|
|
||||||
mockDate.install(initialDate);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.setTimeout = function(fn, delay, params) {
|
|
||||||
if (legacyIE()) {
|
|
||||||
if (arguments.length > 2) {
|
|
||||||
throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill');
|
|
||||||
}
|
|
||||||
return timer.setTimeout(fn, delay);
|
|
||||||
}
|
|
||||||
return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.setInterval = function(fn, delay, params) {
|
|
||||||
if (legacyIE()) {
|
|
||||||
if (arguments.length > 2) {
|
|
||||||
throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill');
|
|
||||||
}
|
|
||||||
return timer.setInterval(fn, delay);
|
|
||||||
}
|
|
||||||
return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.clearTimeout = function(id) {
|
|
||||||
return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.clearInterval = function(id) {
|
|
||||||
return Function.prototype.call.apply(timer.clearInterval, [global, id]);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tick = function(millis) {
|
|
||||||
if (installed) {
|
|
||||||
mockDate.tick(millis);
|
|
||||||
delayedFunctionScheduler.tick(millis);
|
|
||||||
} else {
|
|
||||||
throw new Error('Mock clock is not installed, use jasmine.clock().install()');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function originalTimingFunctionsIntact() {
|
|
||||||
return global.setTimeout === realTimingFunctions.setTimeout &&
|
|
||||||
global.clearTimeout === realTimingFunctions.clearTimeout &&
|
|
||||||
global.setInterval === realTimingFunctions.setInterval &&
|
|
||||||
global.clearInterval === realTimingFunctions.clearInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
function legacyIE() {
|
|
||||||
//if these methods are polyfilled, apply will be present
|
|
||||||
return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
|
|
||||||
}
|
|
||||||
|
|
||||||
function replace(dest, source) {
|
|
||||||
for (const prop in source) {
|
|
||||||
dest[prop] = source[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTimeout(fn, delay) {
|
|
||||||
return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearTimeout(id) {
|
|
||||||
return delayedFunctionScheduler.removeFunctionWithId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInterval(fn, interval) {
|
|
||||||
return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearInterval(id) {
|
|
||||||
return delayedFunctionScheduler.removeFunctionWithId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function argSlice(argsObj, n) {
|
|
||||||
return Array.prototype.slice.call(argsObj, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Clock;
|
|
||||||
}();
|
|
||||||
|
|
||||||
|
|
||||||
j$.DelayedFunctionScheduler = function() {
|
|
||||||
function DelayedFunctionScheduler() {
|
|
||||||
const self = this;
|
|
||||||
const scheduledLookup = [];
|
|
||||||
const scheduledFunctions = {};
|
|
||||||
let currentTime = 0;
|
|
||||||
let delayedFnCount = 0;
|
|
||||||
|
|
||||||
self.tick = function(millis) {
|
|
||||||
millis = millis || 0;
|
|
||||||
const endTime = currentTime + millis;
|
|
||||||
|
|
||||||
runScheduledFunctions(endTime);
|
|
||||||
currentTime = endTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
|
|
||||||
let f;
|
|
||||||
if (typeof(funcToCall) === 'string') {
|
|
||||||
/* jshint evil: true */
|
|
||||||
f = function() { return eval(funcToCall); };
|
|
||||||
/* jshint evil: false */
|
|
||||||
} else {
|
|
||||||
f = funcToCall;
|
|
||||||
}
|
|
||||||
|
|
||||||
millis = millis || 0;
|
|
||||||
timeoutKey = timeoutKey || ++delayedFnCount;
|
|
||||||
runAtMillis = runAtMillis || (currentTime + millis);
|
|
||||||
|
|
||||||
const funcToSchedule = {
|
|
||||||
runAtMillis: runAtMillis,
|
|
||||||
funcToCall: f,
|
|
||||||
recurring: recurring,
|
|
||||||
params: params,
|
|
||||||
timeoutKey: timeoutKey,
|
|
||||||
millis: millis,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (runAtMillis in scheduledFunctions) {
|
|
||||||
scheduledFunctions[runAtMillis].push(funcToSchedule);
|
|
||||||
} else {
|
|
||||||
scheduledFunctions[runAtMillis] = [funcToSchedule];
|
|
||||||
scheduledLookup.push(runAtMillis);
|
|
||||||
scheduledLookup.sort(function(a, b) {
|
|
||||||
return a - b;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeoutKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.removeFunctionWithId = function(timeoutKey) {
|
|
||||||
for (const runAtMillis in scheduledFunctions) {
|
|
||||||
const funcs = scheduledFunctions[runAtMillis];
|
|
||||||
const i = indexOfFirstToPass(funcs, function(func) {
|
|
||||||
return func.timeoutKey === timeoutKey;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (i > -1) {
|
|
||||||
if (funcs.length === 1) {
|
|
||||||
delete scheduledFunctions[runAtMillis];
|
|
||||||
deleteFromLookup(runAtMillis);
|
|
||||||
} else {
|
|
||||||
funcs.splice(i, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// intervals get rescheduled when executed, so there's never more
|
|
||||||
// than a single scheduled function with a given timeoutKey
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function indexOfFirstToPass(array, testFn) {
|
|
||||||
let index = -1;
|
|
||||||
|
|
||||||
for (let i = 0; i < array.length; ++i) {
|
|
||||||
if (testFn(array[i])) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteFromLookup(key) {
|
|
||||||
const value = Number(key);
|
|
||||||
const i = indexOfFirstToPass(scheduledLookup, function(millis) {
|
|
||||||
return millis === value;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (i > -1) {
|
|
||||||
scheduledLookup.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function reschedule(scheduledFn) {
|
|
||||||
self.scheduleFunction(scheduledFn.funcToCall,
|
|
||||||
scheduledFn.millis,
|
|
||||||
scheduledFn.params,
|
|
||||||
true,
|
|
||||||
scheduledFn.timeoutKey,
|
|
||||||
scheduledFn.runAtMillis + scheduledFn.millis);
|
|
||||||
}
|
|
||||||
|
|
||||||
function forEachFunction(funcsToRun, callback) {
|
|
||||||
for (let i = 0; i < funcsToRun.length; ++i) {
|
|
||||||
callback(funcsToRun[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function runScheduledFunctions(endTime) {
|
|
||||||
if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
currentTime = scheduledLookup.shift();
|
|
||||||
|
|
||||||
const funcsToRun = scheduledFunctions[currentTime];
|
|
||||||
delete scheduledFunctions[currentTime];
|
|
||||||
|
|
||||||
forEachFunction(funcsToRun, function(funcToRun) {
|
|
||||||
if (funcToRun.recurring) {
|
|
||||||
reschedule(funcToRun);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
forEachFunction(funcsToRun, function(funcToRun) {
|
|
||||||
funcToRun.funcToCall.apply(null, funcToRun.params || []);
|
|
||||||
});
|
|
||||||
} while (scheduledLookup.length > 0 &&
|
|
||||||
// checking first if we're out of time prevents setTimeout(0)
|
|
||||||
// scheduled in a funcToRun from forcing an extra iteration
|
|
||||||
currentTime !== endTime &&
|
|
||||||
scheduledLookup[0] <= endTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DelayedFunctionScheduler;
|
|
||||||
}();
|
|
||||||
|
|
||||||
|
|
||||||
j$.MockDate = function() {
|
|
||||||
function MockDate(global) {
|
|
||||||
const self = this;
|
|
||||||
let currentTime = 0;
|
|
||||||
|
|
||||||
if (!global || !global.Date) {
|
|
||||||
self.install = function() {};
|
|
||||||
self.tick = function() {};
|
|
||||||
self.uninstall = function() {};
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GlobalDate = global.Date;
|
|
||||||
|
|
||||||
self.install = function(mockDate) {
|
|
||||||
if (mockDate instanceof GlobalDate) {
|
|
||||||
currentTime = mockDate.getTime();
|
|
||||||
} else {
|
|
||||||
currentTime = new GlobalDate().getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Date = FakeDate;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tick = function(millis) {
|
|
||||||
millis = millis || 0;
|
|
||||||
currentTime = currentTime + millis;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.uninstall = function() {
|
|
||||||
currentTime = 0;
|
|
||||||
global.Date = GlobalDate;
|
|
||||||
};
|
|
||||||
|
|
||||||
createDateProperties();
|
|
||||||
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function FakeDate() {
|
|
||||||
switch(arguments.length) {
|
|
||||||
case 0:
|
|
||||||
return new GlobalDate(currentTime);
|
|
||||||
case 1:
|
|
||||||
return new GlobalDate(arguments[0]);
|
|
||||||
case 2:
|
|
||||||
return new GlobalDate(arguments[0], arguments[1]);
|
|
||||||
case 3:
|
|
||||||
return new GlobalDate(arguments[0], arguments[1], arguments[2]);
|
|
||||||
case 4:
|
|
||||||
return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
||||||
case 5:
|
|
||||||
return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
|
|
||||||
arguments[4]);
|
|
||||||
case 6:
|
|
||||||
return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
|
|
||||||
arguments[4], arguments[5]);
|
|
||||||
default:
|
|
||||||
return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
|
|
||||||
arguments[4], arguments[5], arguments[6]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDateProperties() {
|
|
||||||
FakeDate.prototype = GlobalDate.prototype;
|
|
||||||
|
|
||||||
FakeDate.now = function() {
|
|
||||||
if (GlobalDate.now) {
|
|
||||||
return currentTime;
|
|
||||||
} else {
|
|
||||||
throw new Error('Browser does not support Date.now()');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
FakeDate.toSource = GlobalDate.toSource;
|
|
||||||
FakeDate.toString = GlobalDate.toString;
|
|
||||||
FakeDate.parse = GlobalDate.parse;
|
|
||||||
FakeDate.UTC = GlobalDate.UTC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MockDate;
|
|
||||||
}();
|
|
||||||
|
|
||||||
const _clock = new j$.Clock(global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global));
|
|
||||||
|
|
||||||
export function clock() {
|
|
||||||
return _clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue