-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change source code to TypeScript (#8)
* Change source code to TypeScript * update build system * update tests * update typescript build
- Loading branch information
Showing
9 changed files
with
4,592 additions
and
7,222 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { EventEmitter } from 'events' | ||
|
||
type Status = 'running' | 'paused' | 'stopped' | ||
|
||
class Timer extends EventEmitter { | ||
private _interval: number | ||
private _stopwatch: boolean | ||
private _duration: number = 0 | ||
private _endTime: number = 0 | ||
private _pauseTime: number = 0 | ||
private _status: Status = 'stopped' | ||
private _timeoutID?: NodeJS.Timeout | ||
|
||
constructor ({ interval = 1000, stopwatch = false } = {}) { | ||
super() | ||
this._interval = interval | ||
this._stopwatch = stopwatch | ||
} | ||
|
||
public start (duration: number, interval?: number) { | ||
if (this.status !== 'stopped') return | ||
if (duration == null) throw new TypeError('Must provide duration parameter') | ||
this._duration = duration | ||
this._endTime = Date.now() + duration | ||
this._changeStatus('running') | ||
this.emit('tick', this._stopwatch ? 0 : this._duration) | ||
this._timeoutID = setInterval(this.tick, interval || this._interval) | ||
} | ||
|
||
public stop () { | ||
if (this._timeoutID) clearInterval(this._timeoutID) | ||
this._changeStatus('stopped') | ||
} | ||
|
||
public pause () { | ||
if (this.status !== 'running') return | ||
this._pauseTime = Date.now() | ||
this._changeStatus('paused') | ||
} | ||
|
||
public resume () { | ||
if (this.status !== 'paused') return | ||
this._endTime += Date.now() - this._pauseTime | ||
this._pauseTime = 0 | ||
this._changeStatus('running') | ||
} | ||
|
||
private _changeStatus (status: Status) { | ||
this._status = status | ||
this.emit('statusChanged', this.status) | ||
} | ||
|
||
private tick = () => { | ||
if (this.status === 'paused') return | ||
if (Date.now() >= this._endTime) { | ||
this.stop() | ||
this.emit('tick', this._stopwatch ? this._duration : 0) | ||
this.emit('done') | ||
} else { | ||
this.emit('tick', this.time) | ||
} | ||
} | ||
|
||
get time () { | ||
if (this.status === 'stopped') return 0 | ||
const time = this.status === 'paused' ? this._pauseTime : Date.now() | ||
const left = this._endTime - time | ||
return this._stopwatch ? this._duration - left : left | ||
} | ||
|
||
get duration () { | ||
return this._duration | ||
} | ||
|
||
get status () { | ||
return this._status | ||
} | ||
} | ||
|
||
export default Timer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// NOTE: This is mostly the same as test.js, | ||
// but is included to test TypeScript type errors. | ||
|
||
import test from 'tape' | ||
import Timer from '.' | ||
|
||
test('countdown ticks', { timeout: 500 }, (t) => { | ||
const timer = new Timer({ interval: 10 }) | ||
let lastms = 51 | ||
|
||
timer.on('tick', (ms) => { | ||
if (lastms === 51) t.equal(ms, 50, 'first update should be 50') | ||
t.ok(ms < lastms, 'time decreasing') | ||
lastms = ms | ||
}) | ||
|
||
timer.on('done', () => { | ||
t.equal(lastms, 0, 'last update should be 0') | ||
t.end() | ||
}) | ||
|
||
timer.start(50) | ||
}) | ||
|
||
test('stopwatch ticks', { timeout: 500 }, (t) => { | ||
const timer = new Timer({ interval: 10, stopwatch: true }) | ||
let lastms = -1 | ||
|
||
timer.on('tick', (ms) => { | ||
if (lastms === -1) t.equal(ms, 0, 'first update should be 0') | ||
t.ok(ms > lastms, 'time increasing') | ||
lastms = ms | ||
}) | ||
|
||
timer.on('done', () => { | ||
t.equal(lastms, 50, 'last update should be 50') | ||
t.end() | ||
}) | ||
|
||
timer.start(50) | ||
}) | ||
|
||
test('stop', (t) => { | ||
const timer = new Timer({ interval: 10 }) | ||
|
||
timer.on('done', () => t.fail()) | ||
|
||
setTimeout(() => { | ||
t.equal(timer.status, 'stopped') | ||
t.end() | ||
}, 100) | ||
|
||
timer.start(50) | ||
t.equal(timer.status, 'running') | ||
timer.stop() | ||
}) | ||
|
||
test('pause and resume', (t) => { | ||
const timer = new Timer({ interval: 10 }) | ||
const startTime = Date.now() | ||
|
||
timer.on('done', () => { | ||
t.ok(Date.now() - startTime > 100, 'paused for at least 100ms') | ||
t.end() | ||
}) | ||
|
||
setTimeout(() => { | ||
t.equal(timer.status, 'paused') | ||
timer.resume() | ||
}, 100) | ||
|
||
timer.start(50) | ||
t.equal(timer.status, 'running') | ||
timer.pause() | ||
}) | ||
|
||
test('state transition', (t) => { | ||
const timer = new Timer({ interval: 10 }) | ||
t.equal(timer.status, 'stopped') | ||
timer.stop() | ||
t.equal(timer.status, 'stopped') | ||
timer.pause() | ||
t.equal(timer.status, 'stopped') | ||
timer.resume() | ||
t.equal(timer.status, 'stopped') | ||
timer.start(20) | ||
t.equal(timer.status, 'running') | ||
timer.pause() | ||
t.equal(timer.status, 'paused') | ||
timer.resume() | ||
t.equal(timer.status, 'running') | ||
timer.stop() | ||
t.equal(timer.status, 'stopped') | ||
timer.start(20) | ||
t.equal(timer.status, 'running') | ||
timer.pause() | ||
timer.stop() | ||
t.equal(timer.status, 'stopped') | ||
timer.start(20) | ||
timer.on('done', () => { | ||
t.equal(timer.status, 'stopped') | ||
t.end() | ||
}) | ||
}) | ||
|
||
test('duration property', (t) => { | ||
const timer = new Timer({ interval: 10 }) | ||
timer.on('done', () => { | ||
t.equal(timer.duration, 50, 'correct last duration') | ||
t.end() | ||
}) | ||
|
||
timer.start(50) | ||
t.equal(timer.duration, 50, 'correct duration') | ||
}) | ||
|
||
test('time property', (t) => { | ||
const run = (stopwatch: boolean) => { | ||
const timer = new Timer({ interval: 10, stopwatch }) | ||
timer.on('tick', (ms) => { | ||
const time = timer.time | ||
// TODO: last ms and time is not equal in stopwatch mode | ||
// because we stop the timer before calling tick to ensure | ||
// that .time won't be less than 0 or greater than duration | ||
if (stopwatch && ms === 50) return | ||
t.ok(time > ms - 5 && time < ms + 5, 'time should be around the ms param') | ||
}) | ||
|
||
timer.on('done', () => { | ||
t.equal(timer.time, 0) | ||
if (stopwatch) t.end() | ||
}) | ||
|
||
t.equal(timer.time, 0) | ||
timer.start(50) | ||
|
||
const rtime = timer.time | ||
timer.pause() | ||
const ptime = timer.time | ||
|
||
t.equal(rtime, ptime) | ||
|
||
setTimeout(() => { | ||
t.equal(ptime, timer.time) | ||
timer.resume() | ||
}, 20) | ||
} | ||
|
||
run(false) | ||
run(true) | ||
}) | ||
|
||
test('override interval', (t) => { | ||
const timer = new Timer({ interval: 1000 }) | ||
let ticks = 0 | ||
|
||
timer.on('tick', (ms) => ticks++) | ||
|
||
timer.on('done', (ms) => { | ||
t.ok(ticks > 5, 'should do extra tick events') | ||
t.end() | ||
}) | ||
|
||
timer.start(100, 10) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.