Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change source code to TypeScript #8

Merged
merged 4 commits into from
Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions bili.config.js

This file was deleted.

11,423 changes: 4,291 additions & 7,132 deletions package-lock.json

Large diffs are not rendered by default.

28 changes: 18 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
"name": "tiny-timer",
"version": "1.3.0",
"description": "Small countdown timer and stopwatch module.",
"main": "dist/tiny-timer.cjs.js",
"module": "dist/tiny-timer.es.js",
"source": "src/tiny-timer.ts",
"main": "dist/tiny-timer.js",
"module": "dist/tiny-timer.mjs",
"types": "dist/tiny-timer.d.ts",
"scripts": {
"clean:dist": "del-cli dist/*",
"build": "npm run clean:dist && bili",
"prepublish": "npm run build",
"test": "standard src/*.js && tape test.js"
"build": "rimraf dist/* && microbundle --target=node --format=es,cjs --external=events",
"test": "npm run lint && tape test.js && npm run test-ts",
"test-ts": "ts-node -O '{\"esModuleInterop\": true, \"module\": \"commonjs\"}' test-ts.ts",
"lint": "tslint --project .",
"prepare": "npm run build",
"prepublishOnly": "npm test"
},
"keywords": [
"countdown",
Expand All @@ -29,10 +33,14 @@
},
"homepage": "https://github.com/mathiasvr/tiny-timer#readme",
"devDependencies": {
"bili": "^3.1.2",
"del-cli": "^1.1.0",
"standard": "*",
"tape": "^4.6.0"
"@types/node": "^12.6.2",
"@types/tape": "^4.2.33",
"microbundle": "^0.11.0",
"rimraf": "^2.6.3",
"tape": "^4.11.0",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"typescript": "^3.5.3"
},
"dependencies": {},
"files": [
Expand Down
75 changes: 0 additions & 75 deletions src/index.js

This file was deleted.

80 changes: 80 additions & 0 deletions src/tiny-timer.ts
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
165 changes: 165 additions & 0 deletions test-ts.ts
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)
})
6 changes: 6 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,9 @@ test('override interval', function (t) {

timer.start(100, 10)
})

test('argument errors', function (t) {
const timer = new Timer()
t.throws(() => timer.start(), TypeError, 'Throw TypeError')
t.end()
})
Loading