Skip to content

Commit

Permalink
Implement tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Babik committed Apr 23, 2024
1 parent 0f859b0 commit 1529425
Show file tree
Hide file tree
Showing 7 changed files with 4,507 additions and 32 deletions.
9 changes: 9 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "jest-environment-jsdom",
collectCoverage: false,
roots: ["<rootDir>/test"],
clearMocks: true,
resetMocks: true,
restoreMocks: true,
};
4,258 changes: 4,258 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
"name": "trace",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/ababik/trace.git"
"type": "git",
"url": "git+https://github.com/ababik/trace.git"
},
"author": "ababik",
"license": "TBD",
"bugs": {
"url": "https://github.com/ababik/trace/issues"
"url": "https://github.com/ababik/trace/issues"
},
"homepage": "https://github.com/ababik/trace#readme",
"devDependencies": {
"typescript": "*"
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "*"
}
}

}
15 changes: 7 additions & 8 deletions src/trace.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const REPORT_WINDOW_URL = "https://ababik.github.io/trace/viewer.html"

class Trace {
export class Trace {
private root: Context = null
private current: Context = null

Expand Down Expand Up @@ -61,6 +60,7 @@ class Trace {
const report: ReportSummary = { timestamp, total, records }
console.log("trace", report)
this.showReportWindow(report)
return report
}

private showReportWindow(report: ReportSummary) {
Expand All @@ -71,7 +71,7 @@ class Trace {
}
}

class Context {
export class Context {
label: string = null

iterations: number = 0
Expand Down Expand Up @@ -106,13 +106,12 @@ class Context {
off() {
const end = performance.now()
if (this.start === 0) return
this.start = 0
const duration = end - this.start
this.start = 0
this.duration += duration
const mean = this.mean
this.mean += (duration - this.mean) / this.calls
this.sumsq += (duration - mean) * (duration - this.mean)
const variance = this.calls > 1 ? this.sumsq / (this.calls - 1) : 0
this.mean = this.duration / this.calls
this.sumsq += duration * duration
const variance = (this.sumsq - (this.duration * this.duration / this.calls)) / this.calls
this.stddev = Math.sqrt(variance)
if (this.first === 0) this.first = duration
if (this.calls === 1) {
Expand Down
28 changes: 14 additions & 14 deletions src/viewer.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
function generate(report: ReportSummary) {
let { records, timestamp, total } = report
const { records, timestamp, total } = report
setTitle(timestamp, total)
let table = document.querySelector(".table-body")
const table = document.querySelector(".table-body")
function appendCell(row: HTMLElement, value: string) {
let cell = document.createElement("div")
const cell = document.createElement("div")
cell.classList.add("table-cell")
cell.innerText = value
row.appendChild(cell)
}
function walk(records: ReportRecord[], level: number) {
for (let record of records) {
let row = document.createElement("div")
for (const record of records) {
const row = document.createElement("div")
table.appendChild(row)

row.dataset["level"] = level.toString()
Expand All @@ -24,9 +24,9 @@ function generate(report: ReportSummary) {
row.classList.add("hidden")
}

let cellLabel = document.createElement("div")
const cellLabel = document.createElement("div")
cellLabel.classList.add("table-cell")
let cellLabelInner = document.createElement("div")
const cellLabelInner = document.createElement("div")
let label = record.label
if (record.records.length !== 0) {
label += ` (${record.records.length})`
Expand Down Expand Up @@ -55,14 +55,14 @@ function toggle(row: HTMLElement, expand = false) {
expand = row.classList.contains("collapsed")
row.classList.toggle("collapsed")
}
let level = +row.dataset["level"]
const level = +row.dataset["level"]
let next = row
while (true) {
next = next.nextElementSibling as HTMLElement
if (!next) {
break
}
let nextLevel = +next.dataset.level
const nextLevel = +next.dataset.level
if (nextLevel <= level) {
break
}
Expand All @@ -80,11 +80,11 @@ function toggle(row: HTMLElement, expand = false) {
}

function setTitle(timestamp: number, total: number) {
let date = new Date(timestamp)
let hours = date.getHours().toString().padStart(2, "0")
let minutes = date.getMinutes().toString().padStart(2, "0")
let seconds = date.getSeconds().toString().padStart(2, "0")
window.document.title = `${hours}:${minutes}:${seconds} - ${Math.round(total)}ms`;
const date = new Date(timestamp)
const hours = date.getHours().toString().padStart(2, "0")
const minutes = date.getMinutes().toString().padStart(2, "0")
const seconds = date.getSeconds().toString().padStart(2, "0")
window.document.title = `${hours}:${minutes}:${seconds} - ${Math.round(total)}ms`
}

window.addEventListener("message", (event: MessageEvent<ReportSummary>) => {
Expand Down
197 changes: 197 additions & 0 deletions test/trace.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { Trace } from "../src/trace"

window.open = jest.fn()

test("no call", () => {
const trace = new Trace()
const report = trace.report()

expect(report).toEqual({
timestamp: expect.any(Number),
total: 0,
records: []
})
})

test("single call", () => {
jest.spyOn(performance, "now")
.mockImplementationOnce(() => 10)
.mockImplementationOnce(() => 20)

const trace = new Trace()
trace.on("block1")
trace.off("block1")
const report = trace.report()

expect(report).toEqual({
timestamp: expect.any(Number),
total: 10,
records: [
{
label: "block1",
calls: 1,
duration: 10,
first: 10,
max: 10,
min: 10,
mean: 10,
stddev: 0,
percent: 100,
records: []
}
]
})
})

test("two calls", () => {
jest.spyOn(performance, "now")
.mockImplementationOnce(() => 10)
.mockImplementationOnce(() => 20)
.mockImplementationOnce(() => 30)
.mockImplementationOnce(() => 50)

const trace = new Trace()
trace.on("block1")
trace.off("block1")
trace.on("block2")
trace.off("block2")
const report = trace.report()

expect(report).toEqual({
timestamp: expect.any(Number),
total: 30,
records: [
{
label: "block1",
calls: 1,
duration: 10,
first: 10,
max: 10,
min: 10,
mean: 10,
stddev: 0,
percent: 33.33333333333333,
records: []
},
{
label: "block2",
calls: 1,
duration: 20,
first: 20,
max: 20,
min: 20,
mean: 20,
stddev: 0,
percent: 66.66666666666666,
records: []
}
]
})
})

test("nested calls", () => {
jest.spyOn(performance, "now")
// block1 - on
.mockImplementationOnce(() => 1)
// sub-block1
.mockImplementationOnce(() => 1)
.mockImplementationOnce(() => 21)
.mockImplementationOnce(() => 1)
.mockImplementationOnce(() => 31)
.mockImplementationOnce(() => 1)
.mockImplementationOnce(() => 11)
// sub-block2
.mockImplementationOnce(() => 1)
.mockImplementationOnce(() => 51)
// block1 - off
.mockImplementationOnce(() => 201)
// block2
.mockImplementationOnce(() => 1)
.mockImplementationOnce(() => 101)

const trace = new Trace()
trace.on("block1")
trace.on("sub-block1")
trace.off("sub-block1")
trace.on("sub-block1")
trace.off("sub-block1")
trace.on("sub-block1")
trace.off("sub-block1")
trace.on("sub-block2")
trace.off("sub-block2")
trace.off("block1")
trace.on("block2")
trace.off("block2")
const report = trace.report()

expect(report).toEqual({
timestamp: expect.any(Number),
total: 300,
records: [
{
calls: 1,
duration: 200,
first: 200,
label: "block1",
max: 200,
mean: 200,
min: 200,
percent: 66.66666666666666,
stddev: 0,
records: [
{
calls: 3,
duration: 60,
first: 20,
label: "sub-block1",
max: 30,
mean: 20,
min: 10,
percent: 20,
stddev: 8.16496580927726,
records: [],
},
{
calls: 1,
duration: 50,
first: 50,
label: "sub-block2",
max: 50,
mean: 50,
min: 50,
percent: 16.666666666666664,
stddev: 0,
records: [],
}
]
},
{
calls: 1,
duration: 100,
first: 100,
label: "block2",
max: 100,
mean: 100,
min: 100,
percent: 33.33333333333333,
stddev: 0,
records: []
}
]
})
})

test("invalid off", () => {
const trace = new Trace()
trace.on("block1")
trace.on("block2")
expect(() => trace.off("block1"))
.toThrow("Unexpected trace label \"block1\" (expected \"block2\").")
})

test("unexpected report call", () => {
const trace = new Trace()
trace.on("block1")
expect(() => trace.report())
.toThrow("Trace \"block1\" is still active.")
})
17 changes: 13 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */

/* Modules */
"module": "ESNext", /* Specify what module code is generated. */
"module": "ESNext", /* Specify what module code is generated. */
"rootDir": "./src", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
Expand Down Expand Up @@ -55,7 +55,7 @@
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./public", /* Specify an output folder for all emitted files. */
"outDir": "./public", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
Expand Down Expand Up @@ -104,6 +104,15 @@

/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"test",
"**/*.test.ts",
"**/*.spec.ts"
]
}

0 comments on commit 1529425

Please sign in to comment.