@jackens/nnn 2024.2.15 → 2024.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/chartable.d.ts CHANGED
@@ -2,32 +2,30 @@
2
2
  * A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
3
3
  *
4
4
  * Options:
5
- * - `bottom`: bottom padding (for X axis labels)
6
- * - `gapX`: X axis spacing
7
- * - `gapY`: Y axis spacing
8
- * - `headerColumn`: flag indicating that `table` has a header column (with X axis labels)
9
- * - `id`: chart id
10
- * - `left`: left padding
11
- * - `maxY`: number of Y axis lines
12
- * - `reverse`: flag to reverse all data series
13
- * - `right`: right padding (for data series labels)
14
- * - `rotate`: flag to rotate X axis labels
15
5
  * - `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
16
- * - `top`: top padding
6
+ * - `headerColumn`: flag indicating that `table` has a header column with X axis labels
7
+ * - `xGap`: X axis spacing
8
+ * - `xLabelsPx`:
9
+ * - `xLabelsRotate`: flag to rotate X axis labels
10
+ * - `xReverse`: flag to reverse all data series
11
+ * - `yGap`: Y axis spacing
12
+ * - `yLabelsLeftPx`:
13
+ * - `yLabelsRightPx`:
14
+ * - `yMax`: number of Y axis lines
15
+ * - `zLabelsPx`:
17
16
  */
18
- export function chartable({ bottom, gapX, gapY, headerColumn, id, left, maxY, reverse, right, rotate, table, top }: {
19
- bottom?: number;
20
- gapX?: number;
21
- gapY?: number;
22
- headerColumn?: boolean;
23
- id?: string;
24
- left?: number;
25
- maxY?: number;
26
- reverse?: boolean;
27
- right?: number;
28
- rotate?: boolean;
17
+ export function chartable({ table, headerColumn, xGap, xLabelsPx, xLabelsRotate, xReverse, yGap, yLabelsLeftPx, yLabelsRightPx, yMax, zLabelsPx }: {
29
18
  table: HTMLTableElement;
30
- top?: number;
19
+ headerColumn?: boolean;
20
+ xGap?: number;
21
+ xLabelsPx?: number;
22
+ xLabelsRotate?: boolean;
23
+ xReverse?: boolean;
24
+ yGap?: number;
25
+ yLabelsLeftPx?: number;
26
+ yLabelsRightPx?: number;
27
+ yMax?: number;
28
+ zLabelsPx?: number;
31
29
  }): SVGSVGElement;
32
30
 
33
31
  export const tests: {};
package/chartable.js CHANGED
@@ -1,54 +1,56 @@
1
- import { s } from './h.js'
1
+ import { h, s } from './h.js'
2
2
 
3
3
  const COLORS = ['#e22', '#e73', '#fc3', '#ad4', '#4d9', '#3be', '#45d', '#c3e']
4
+ const PADDING = 15
5
+
6
+ const _svg = s('svg', { viewBox: '0 0 0 0', width: 0, height: 0 })
7
+
8
+ h(document.body, ['div', { $style: { width: 0, height: 0 } }, _svg])
4
9
 
5
10
  /**
6
11
  * A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
7
12
  *
8
13
  * Options:
9
- * - `bottom`: bottom padding (for X axis labels)
10
- * - `gapX`: X axis spacing
11
- * - `gapY`: Y axis spacing
12
- * - `headerColumn`: flag indicating that `table` has a header column (with X axis labels)
13
- * - `id`: chart id
14
- * - `left`: left padding
15
- * - `maxY`: number of Y axis lines
16
- * - `reverse`: flag to reverse all data series
17
- * - `right`: right padding (for data series labels)
18
- * - `rotate`: flag to rotate X axis labels
19
14
  * - `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
20
- * - `top`: top padding
15
+ * - `headerColumn`: flag indicating that `table` has a header column with X axis labels
16
+ * - `xGap`: X axis spacing
17
+ * - `xLabelsPx`:
18
+ * - `xLabelsRotate`: flag to rotate X axis labels
19
+ * - `xReverse`: flag to reverse all data series
20
+ * - `yGap`: Y axis spacing
21
+ * - `yLabelsLeftPx`:
22
+ * - `yLabelsRightPx`:
23
+ * - `yMax`: number of Y axis lines
24
+ * - `zLabelsPx`:
21
25
  *
22
26
  * @param {{
23
- * bottom?: number;
24
- * gapX?: number;
25
- * gapY?: number;
26
- * headerColumn?: boolean;
27
- * id?: string;
28
- * left?: number;
29
- * maxY?: number;
30
- * reverse?: boolean;
31
- * right?: number;
32
- * rotate?: boolean;
33
27
  * table: HTMLTableElement;
34
- * top?: number;
28
+ * headerColumn?: boolean;
29
+ * xGap?: number;
30
+ * xLabelsPx?: number;
31
+ * xLabelsRotate?: boolean;
32
+ * xReverse?: boolean;
33
+ * yGap?: number;
34
+ * yLabelsLeftPx?: number;
35
+ * yLabelsRightPx?: number;
36
+ * yMax?: number;
37
+ * zLabelsPx?: number;
35
38
  * }} options
36
39
  */
37
40
  export const chartable = ({
38
- bottom = 50,
39
- gapX = 70,
40
- gapY = 30,
41
- headerColumn = false,
42
- id,
43
- left = 15,
44
- maxY = 10,
45
- reverse = false,
46
- right = 200,
47
- rotate = false,
48
41
  table,
49
- top = 15
42
+ headerColumn = false,
43
+ xGap = 70,
44
+ xLabelsPx = 0,
45
+ xLabelsRotate = false,
46
+ xReverse = false,
47
+ yGap = 30,
48
+ yLabelsLeftPx = 100,
49
+ yLabelsRightPx = 100,
50
+ yMax = 10,
51
+ zLabelsPx = 0
50
52
  }) => {
51
- const /** @type {number[][]} */ zxY = []
53
+ const /** @type {number[][]} */ zxValues = []
52
54
  const /** @type {string[]} */ xLabels = []
53
55
  const /** @type {string[]} */ zLabels = []
54
56
 
@@ -60,8 +62,8 @@ export const chartable = ({
60
62
  const value = col.innerText
61
63
 
62
64
  if (x >= 0 && z >= 0) {
63
- zxY[z] = zxY[z] ?? []
64
- zxY[z][x] = parseFloat(value)
65
+ zxValues[z] = zxValues[z] ?? []
66
+ zxValues[z][x] = parseFloat(value)
65
67
  } else if (x >= 0 && z < 0) {
66
68
  xLabels[x] = value
67
69
  } else if (x < 0 && z >= 0) {
@@ -69,62 +71,123 @@ export const chartable = ({
69
71
  }
70
72
  }))
71
73
 
72
- if (reverse) {
74
+ if (xReverse) {
73
75
  xLabels.reverse()
74
- zxY.forEach(xY => xY.reverse())
76
+ zxValues.forEach(xValues => xValues.reverse())
77
+ }
78
+
79
+ const xMax = zxValues[0].length
80
+ const xPx = Array.from({ length: xMax }, (_, x) => x * xGap)
81
+ const yPx = Array.from({ length: yMax }, (_, y) => y * yGap)
82
+
83
+ const width = xGap * (xMax - 1)
84
+ const height = yGap * (yMax - 1)
85
+
86
+ const gGraph = s('g',
87
+ ...yPx.map(px => ['line', { x1: 0, x2: width, y1: px, y2: px, stroke: '#8888' }]),
88
+ ...xPx.map(px => ['line', { x1: px, x2: px, y1: 0, y2: height, stroke: '#8888' }]))
89
+ const gXLabels = s('g')
90
+ const gYLabelsL = zxValues.map(_ => s('g'))
91
+ const gYLabelsR = zxValues.map(_ => s('g'))
92
+ const gZColors = s('g')
93
+ const /** @type {SVGTextElement[]} */ gZLabels = []
94
+
95
+ const onclick = (/** @type {number} */ z) => {
96
+ gYLabelsL.forEach((g, z2) => s(g, { $style: { display: z2 === z ? 'block' : 'none' } }))
97
+ gYLabelsR.forEach((g, z2) => s(g, { $style: { display: z2 === z ? 'block' : 'none' } }))
98
+ gZLabels.forEach((text, z2) => s(text, { $style: { fontWeight: z2 === z ? 'bold' : 'normal', cursor: 'pointer' } }))
75
99
  }
76
100
 
77
- const maxX = zxY[0].length
78
- const xi = Array.from({ length: maxX }, (_, x) => x * gapX)
79
- const yi = Array.from({ length: maxY }, (_, y) => y * gapY)
80
- const w = gapX * (maxX - 1)
81
- const h = gapY * (maxY - 1)
82
- const svgW = left + w + right
83
- const svgH = top + h + bottom
84
- const /** @type {import('./h.js').HArgs1} */ graph = ['g', { transform: `translate(${left} ${top})` },
85
- ...yi.map(y => ['line', { x1: 0, x2: w, y1: y, y2: y, stroke: '#8888' }]),
86
- ...xi.map(x => ['line', { x1: x, x2: x, y1: 0, y2: h, stroke: '#8888' }])]
87
- const /** @type {import('./h.js').HArgs1} */ colors = ['g', { transform: `translate(${left + w + 25} ${top + 15})` }]
88
- const /** @type {import('./h.js').HArgs1} */ labels = ['g', { transform: `translate(${left + w + 40} ${top + 15})` }]
89
-
90
- zxY.forEach((xY, z) => {
101
+ zxValues.forEach((values, z) => {
91
102
  const fill = COLORS[z % COLORS.length]
92
- const min = Math.min(...xY.filter(x => !isNaN(x)))
93
- const max = Math.max(...xY.filter(x => !isNaN(x)))
103
+ const minValue = Math.min(...values.filter(value => !isNaN(value)))
104
+ const maxValue = Math.max(...values.filter(value => !isNaN(value)))
105
+
106
+ yPx.forEach((yp, y) => {
107
+ const textL = s('text',
108
+ { x: 0, y: yp, 'text-anchor': 'end', 'alignment-baseline': 'middle' },
109
+ (maxValue - (maxValue - minValue) / yMax * (y + 1)).toFixed(2))
110
+ const textR = s('text',
111
+ { x: 0, y: yp, 'text-anchor': 'start', 'alignment-baseline': 'middle' },
112
+ (maxValue - (maxValue - minValue) / yMax * (y + 1)).toFixed(2))
113
+
114
+ s(_svg, textL, textR)
115
+
116
+ yLabelsLeftPx = Math.max(yLabelsLeftPx, textL.getBBox().width)
117
+ yLabelsRightPx = Math.max(yLabelsRightPx, textL.getBBox().width)
118
+
119
+ s(gYLabelsL[z], textL)
120
+ s(gYLabelsR[z], textR)
121
+ })
94
122
 
95
- colors.push(['circle', { cx: 0, cy: gapY * (colors.length - 2), r: 5, fill }])
96
- labels.push(['text',
97
- { x: 0, y: gapY * (labels.length - 2), 'text-anchor': 'start', 'alignment-baseline': 'middle' },
98
- zLabels[z]])
123
+ const text = s('text', {
124
+ x: 0,
125
+ y: yGap * z,
126
+ 'text-anchor': 'start',
127
+ 'alignment-baseline': 'middle',
128
+ $onclick: () => onclick(z),
129
+ $style: { fontWeight: 'bold' }
130
+ },
131
+ zLabels[z])
99
132
 
100
- const y = xY.map(x => isNaN(x) ? x : h * (max - x) / (max - min))
133
+ s(_svg, text)
101
134
 
102
- xi.forEach((x, i) => {
103
- if (!isNaN(y[i])) {
104
- graph.push(['g', ['circle', { cx: x, cy: y[i], r: 5, fill }, ['title', `${zLabels[z]}: ${xY[i]}`]]])
135
+ zLabelsPx = Math.max(zLabelsPx, text.getBBox().width)
105
136
 
106
- if (!isNaN(y[i - 1])) {
107
- graph.push(['line', { x1: x, x2: xi[i - 1], y1: y[i], y2: y[i - 1], stroke: fill }])
137
+ gZLabels[z] = text
138
+
139
+ s(gZColors, ['circle', { cx: 0, cy: yGap * z, r: 5, fill }])
140
+
141
+ const valuesPx = values.map(value => isNaN(value) ? value : height * (maxValue - value) / (maxValue - minValue))
142
+
143
+ xPx.forEach((px, x) => {
144
+ if (!isNaN(valuesPx[x])) {
145
+ s(gGraph, ['g', ['circle', { cx: px, cy: valuesPx[x], r: 5, fill }]])
146
+
147
+ if (!isNaN(valuesPx[x - 1])) {
148
+ s(gGraph, ['line', { x1: px, x2: xPx[x - 1], y1: valuesPx[x], y2: valuesPx[x - 1], stroke: fill }])
108
149
  }
109
150
  }
110
151
  })
111
152
  })
112
153
 
113
- return s('svg', {
114
- viewBox: `0 0 ${svgW} ${svgH}`,
115
- width: `${svgW}px`,
116
- height: `${svgH}px`,
117
- class: 'chartable'
118
- }, id != null ? { id } : null,
119
- graph, colors, labels,
120
- xLabels.length > 0
121
- ? ['g', { transform: `translate(${left} ${top + h + 15})` },
122
- ...xi.slice(0).map((x, i) => ['text', rotate
123
- ? { x: 0, y: -x, 'text-anchor': 'start', 'alignment-baseline': 'hanging', transform: 'rotate(90)' }
124
- : { x, y: 0, 'text-anchor': 'middle', 'alignment-baseline': 'hanging' },
125
- xLabels[i]])]
126
- : null
127
- )
154
+ if (xLabels.length > 0) {
155
+ xPx.forEach((xp, x) => {
156
+ const text = s('text',
157
+ xLabelsRotate
158
+ ? { x: 0, y: -xp, 'text-anchor': 'start', 'alignment-baseline': 'middle', transform: 'rotate(90)' }
159
+ : { x: xp, y: PADDING, 'text-anchor': 'middle', 'alignment-baseline': 'middle' },
160
+ xLabels[x])
161
+
162
+ s(_svg, text)
163
+
164
+ xLabelsPx = Math.max(xLabelsPx, xLabelsRotate ? text.getBBox().width : text.getBBox().height)
165
+
166
+ s(gXLabels, text)
167
+ })
168
+ }
169
+
170
+ const svgW = width + yLabelsLeftPx + yLabelsRightPx + zLabelsPx + 4 * PADDING
171
+ const svgH = height + xLabelsPx + (xLabels.length > 0 ? 3 : 2) * PADDING
172
+
173
+ s(gGraph, { transform: `translate(${yLabelsLeftPx + PADDING} ${PADDING})` })
174
+
175
+ gYLabelsL.forEach(g => s(g, { transform: `translate(${yLabelsLeftPx} ${PADDING})` }))
176
+ gYLabelsR.forEach(g => s(g, { transform: `translate(${width + yLabelsLeftPx + 2 * PADDING} ${PADDING})` }))
177
+
178
+ s(gZColors, { transform: `translate(${width + yLabelsLeftPx + yLabelsRightPx + 3 * PADDING} ${PADDING})` })
179
+ s(gXLabels, { transform: `translate(${yLabelsLeftPx + PADDING} ${height + 2 * PADDING})` })
180
+
181
+ onclick(0)
182
+
183
+ return s('svg',
184
+ { viewBox: `0 0 ${svgW} ${svgH}`, width: `${svgW}px`, height: `${svgH}px`, class: 'chartable' },
185
+ gGraph,
186
+ gXLabels,
187
+ ...gYLabelsL,
188
+ ...gYLabelsR,
189
+ gZColors,
190
+ ['g', { transform: `translate(${width + yLabelsLeftPx + yLabelsRightPx + 4 * PADDING} ${PADDING})` }, ...gZLabels])
128
191
  }
129
192
 
130
193
  export const tests = {}
package/eq.d.ts CHANGED
@@ -4,5 +4,5 @@
4
4
  export function eq(x: any, y: any): boolean;
5
5
 
6
6
  export namespace tests {
7
- function eq(expect: import("bun:test").Expect): void;
7
+ function eq(expect: Chai.ExpectStatic): void;
8
8
  }
package/eq.js CHANGED
@@ -45,39 +45,39 @@ export const eq = /** @return {boolean} */ (/** @type {any} */ x, /** @type {any
45
45
  }
46
46
 
47
47
  export const tests = {
48
- eq: (/** @type {import('bun:test').Expect} */ expect) => {
49
- expect(eq(true, true)).toBe(true)
50
- expect(eq(NaN, NaN)).toBe(true)
51
- expect(eq(null, undefined)).toBe(false)
52
- expect(eq(42, 42)).toBe(true)
48
+ eq: (/** @type {import('chai').expect} */ expect) => {
49
+ expect(eq(true, true)).to.be.true
50
+ expect(eq(NaN, NaN)).to.be.true
51
+ expect(eq(null, undefined)).to.be.false
52
+ expect(eq(42, 42)).to.be.true
53
53
  // eslint-disable-next-line no-new-wrappers
54
- expect(eq(42, new Number(42))).toBe(true)
55
- expect(eq(42, Number(42))).toBe(true)
54
+ expect(eq(42, new Number(42))).to.be.true
55
+ expect(eq(42, Number(42))).to.be.true
56
56
  // eslint-disable-next-line no-new-wrappers
57
- expect(eq(new Number(42), Number(42))).toBe(true)
58
- expect(eq(42, '42')).toBe(false)
59
- expect(eq('42', '42')).toBe(true)
57
+ expect(eq(new Number(42), Number(42))).to.be.true
58
+ expect(eq(42, '42')).to.be.false
59
+ expect(eq('42', '42')).to.be.true
60
60
  // eslint-disable-next-line no-new-wrappers
61
- expect(eq('42', new String('42'))).toBe(true)
62
- expect(eq('42', String('42'))).toBe(true)
61
+ expect(eq('42', new String('42'))).to.be.true
62
+ expect(eq('42', String('42'))).to.be.true
63
63
  // eslint-disable-next-line no-new-wrappers
64
- expect(eq(String('42'), new String('42'))).toBe(true)
65
- expect(eq(/42/, /42/)).toBe(true)
66
- expect(eq(/42/, /42/g)).toBe(false)
67
- expect(eq(new Date(42), new Date(42))).toBe(true)
68
- expect(eq(new Date(), new Date(42))).toBe(false)
69
- expect(eq({ j: '42', c: 42 }, { c: 42, j: '42' })).toBe(true)
70
- expect(eq([42, '42'], [42, '42'])).toBe(true)
71
- expect(eq(new Set(['42', 42]), new Set([42, '42']))).toBe(true)
72
- expect(eq(new Set(['42', 42]), new Set([42]))).toBe(false)
73
- expect(eq(new Set([42, undefined]), new Set([42]))).toBe(false)
64
+ expect(eq(String('42'), new String('42'))).to.be.true
65
+ expect(eq(/42/, /42/)).to.be.true
66
+ expect(eq(/42/, /42/g)).to.be.false
67
+ expect(eq(new Date(42), new Date(42))).to.be.true
68
+ expect(eq(new Date(), new Date(42))).to.be.false
69
+ expect(eq({ j: '42', c: 42 }, { c: 42, j: '42' })).to.be.true
70
+ expect(eq([42, '42'], [42, '42'])).to.be.true
71
+ expect(eq(new Set(['42', 42]), new Set([42, '42']))).to.be.true
72
+ expect(eq(new Set(['42', 42]), new Set([42]))).to.be.false
73
+ expect(eq(new Set([42, undefined]), new Set([42]))).to.be.false
74
74
  expect(eq(
75
75
  new Map([[{ j: 42 }, { J: '42' }], [{ c: 42 }, { C: '42' }]]),
76
76
  new Map([[{ c: 42 }, { C: '42' }], [{ j: 42 }, { J: '42' }]])
77
- )).toBe(true)
77
+ )).to.be.true
78
78
  expect(eq(
79
79
  new Map([[{ j: 42 }, { J: '42' }], [{ c: 42 }, { C: '42' }]]),
80
80
  new Map([[{ j: '42' }, { J: 42 }], [{ c: '42' }, { C: 42 }]])
81
- )).toBe(false)
81
+ )).to.be.false
82
82
  }
83
83
  }
package/escape.d.ts CHANGED
@@ -14,5 +14,5 @@ export function escape(escapeMap: EscapeMap, template: TemplateStringsArray, ...
14
14
  export function escapeValues(escapeMap: EscapeMap, values: any[]): string[];
15
15
 
16
16
  export namespace tests {
17
- function escape(expect: import("bun:test").Expect): void;
17
+ function escape(expect: Chai.ExpectStatic): void;
18
18
  }
package/escape.js CHANGED
@@ -22,7 +22,7 @@ export const escape = (
22
22
  ) => String.raw(template, ...escapeValues(escapeMap, values))
23
23
 
24
24
  export const tests = {
25
- escape: (/** @type {import('bun:test').Expect} */ expect) => {
25
+ escape: (/** @type {import('chai').expect} */ expect) => {
26
26
  // @ts-expect-error
27
27
  const /** @type {EscapeMap} */ escapeMap = new Map([
28
28
  [undefined, () => 'NULL'],
@@ -46,6 +46,6 @@ export const tests = {
46
46
  FROM table_name
47
47
  WHERE column_name IN (b'1', NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`
48
48
 
49
- expect(actual).toEqual(expected)
49
+ expect(actual).to.deep.equal(expected)
50
50
  }
51
51
  }
@@ -4,5 +4,5 @@
4
4
  export function fixTypography(node: Node): void;
5
5
 
6
6
  export namespace tests {
7
- function fixTypography(expect: import("bun:test").Expect): void;
7
+ function fixTypography(expect: Chai.ExpectStatic): void;
8
8
  }
package/fixTypography.js CHANGED
@@ -49,12 +49,12 @@ export const fixTypography = (/** @type {Node} */ node) => {
49
49
  }
50
50
 
51
51
  export const tests = {
52
- fixTypography: (/** @type {import('bun:test').Expect} */ expect) => {
52
+ fixTypography: (/** @type {import('chai').expect} */ expect) => {
53
53
  const p = h('p', 'Pchnąć w tę łódź jeża lub ośm skrzyń fig (zob. https://pl.wikipedia.org/wiki/Pangram).')
54
54
 
55
55
  fixTypography(p)
56
56
 
57
- expect(p.innerHTML).toEqual(
57
+ expect(p.innerHTML).to.deep.equal(
58
58
  'Pchnąć <span style="white-space:nowrap">w </span>tę łódź jeża lub ośm skrzyń fig ' +
59
59
  '(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/\u200Bwiki/\u200BPangram).')
60
60
  }
package/h.d.ts CHANGED
@@ -47,9 +47,9 @@ export function s<N extends Node>(node: N, ...args1: HArgs1[]): N;
47
47
  export function s(tagOrNode: string | Node, ...args1: HArgs1[]): Node;
48
48
 
49
49
  export const tests: {
50
- h: (expect: import('bun:test').Expect) => void;
51
- 'h: innerText vs items': (expect: import('bun:test').Expect) => void;
52
- 'h: style': (expect: import('bun:test').Expect) => void;
53
- 'h: attributes vs properties': (expect: import('bun:test').Expect) => void;
54
- 'h: nested properties': (expect: import('bun:test').Expect) => void;
50
+ h: (expect: Chai.ExpectStatic) => void;
51
+ 'h: innerText vs items': (expect: Chai.ExpectStatic) => void;
52
+ 'h: style': (expect: Chai.ExpectStatic) => void;
53
+ 'h: attributes vs properties': (expect: Chai.ExpectStatic) => void;
54
+ 'h: nested properties': (expect: Chai.ExpectStatic) => void;
55
55
  };
package/h.js CHANGED
@@ -142,72 +142,72 @@ export const h = _h()
142
142
  export const s = _h('http://www.w3.org/2000/svg')
143
143
 
144
144
  export const tests = {
145
- h: (/** @type {import('bun:test').Expect} */ expect) => {
145
+ h: (/** @type {import('chai').expect} */ expect) => {
146
146
  const b = h('b')
147
147
 
148
- expect(b.outerHTML).toEqual('<b></b>')
148
+ expect(b.outerHTML).to.deep.equal('<b></b>')
149
149
 
150
150
  const i = h('i', 'text')
151
151
 
152
152
  h(b, i)
153
153
 
154
- expect(i.outerHTML).toEqual('<i>text</i>')
155
- expect(b.outerHTML).toEqual('<b><i>text</i></b>')
154
+ expect(i.outerHTML).to.deep.equal('<i>text</i>')
155
+ expect(b.outerHTML).to.deep.equal('<b><i>text</i></b>')
156
156
 
157
157
  h(i, { $className: 'some class' })
158
158
 
159
- expect(i.outerHTML).toEqual('<i class="some class">text</i>')
160
- expect(b.outerHTML).toEqual('<b><i class="some class">text</i></b>')
159
+ expect(i.outerHTML).to.deep.equal('<i class="some class">text</i>')
160
+ expect(b.outerHTML).to.deep.equal('<b><i class="some class">text</i></b>')
161
161
  },
162
162
 
163
- 'h: innerText vs items': (/** @type {import('bun:test').Expect} */ expect) => {
164
- expect(h('span', 'text').outerHTML).toEqual('<span>text</span>')
165
- expect(h('span', { $innerText: 'text' }).outerHTML).toEqual('<span>text</span>')
163
+ 'h: innerText vs items': (/** @type {import('chai').expect} */ expect) => {
164
+ expect(h('span', 'text').outerHTML).to.deep.equal('<span>text</span>')
165
+ expect(h('span', { $innerText: 'text' }).outerHTML).to.deep.equal('<span>text</span>')
166
166
  },
167
167
 
168
- 'h: style': (/** @type {import('bun:test').Expect} */ expect) => {
168
+ 'h: style': (/** @type {import('chai').expect} */ expect) => {
169
169
  expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
170
- .toEqual('<div style="margin:0;padding:0"></div>')
170
+ .to.deep.equal('<div style="margin:0;padding:0"></div>')
171
171
  expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
172
- .toEqual('<div style="margin: 0px; padding: 0px;"></div>')
172
+ .to.deep.equal('<div style="margin: 0px; padding: 0px;"></div>')
173
173
  expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML)
174
- .toEqual('<div style="margin: 0px; padding: 0px;"></div>')
174
+ .to.deep.equal('<div style="margin: 0px; padding: 0px;"></div>')
175
175
  },
176
176
 
177
- 'h: attributes vs properties': (/** @type {import('bun:test').Expect} */ expect) => {
177
+ 'h: attributes vs properties': (/** @type {import('chai').expect} */ expect) => {
178
178
  const input1 = h('input', { value: 42 })
179
179
  const input2 = h('input', { $value: '42' })
180
180
 
181
- expect(input1.value).toEqual('42')
182
- expect(input2.value).toEqual('42')
181
+ expect(input1.value).to.deep.equal('42')
182
+ expect(input2.value).to.deep.equal('42')
183
183
 
184
- expect(input1.outerHTML).toEqual('<input value="42">')
185
- expect(input2.outerHTML).toEqual('<input>')
184
+ expect(input1.outerHTML).to.deep.equal('<input value="42">')
185
+ expect(input2.outerHTML).to.deep.equal('<input>')
186
186
 
187
187
  const checkbox1 = h('input', { type: 'checkbox', checked: true })
188
188
  const checkbox2 = h('input', { type: 'checkbox', $checked: true })
189
189
 
190
- expect(checkbox1.checked).toBe(true)
191
- expect(checkbox2.checked).toBe(true)
190
+ expect(checkbox1.checked).to.be.true
191
+ expect(checkbox2.checked).to.be.true
192
192
 
193
- expect(checkbox1.outerHTML).toEqual('<input type="checkbox" checked="">')
194
- expect(checkbox2.outerHTML).toEqual('<input type="checkbox">')
193
+ expect(checkbox1.outerHTML).to.deep.equal('<input type="checkbox" checked="">')
194
+ expect(checkbox2.outerHTML).to.deep.equal('<input type="checkbox">')
195
195
  },
196
196
 
197
- 'h: nested properties': (/** @type {import('bun:test').Expect} */ expect) => {
197
+ 'h: nested properties': (/** @type {import('chai').expect} */ expect) => {
198
198
  const div = h('div')
199
199
 
200
200
  // @ts-expect-error
201
- expect(div.key).toBeUndefined()
201
+ expect(div.key).to.be.undefined
202
202
 
203
203
  h(div, { $key: { one: 1 } })
204
204
 
205
205
  // @ts-expect-error
206
- expect(div.key).toEqual({ one: 1 })
206
+ expect(div.key).to.deep.equal({ one: 1 })
207
207
 
208
208
  h(div, { $key: { two: 2 } })
209
209
 
210
210
  // @ts-expect-error
211
- expect(div.key).toEqual({ one: 1, two: 2 })
211
+ expect(div.key).to.deep.equal({ one: 1, two: 2 })
212
212
  }
213
213
  }
package/has.d.ts CHANGED
@@ -4,6 +4,6 @@
4
4
  export function has(key: any, ref: any): boolean;
5
5
 
6
6
  export const tests: {
7
- has: (expect: import('bun:test').Expect) => void;
8
- 'has: null': (expect: import('bun:test').Expect) => void;
7
+ has: (expect: Chai.ExpectStatic) => void;
8
+ 'has: null': (expect: Chai.ExpectStatic) => void;
9
9
  };
package/has.js CHANGED
@@ -7,24 +7,24 @@ export const has = (/** @type {any} */ key, /** @type {any} */ ref) =>
7
7
  (is(String, key) || is(Number, key) || is(Symbol, key)) && Object.hasOwnProperty.call(ref ?? Object, key)
8
8
 
9
9
  export const tests = {
10
- has: (/** @type {import('bun:test').Expect} */ expect) => {
10
+ has: (/** @type {import('chai').expect} */ expect) => {
11
11
  const obj = { key: 'K', null: 'N' }
12
12
 
13
- expect('key' in obj).toBe(true)
14
- expect(has('key', obj)).toBe(true)
13
+ expect('key' in obj).to.be.true
14
+ expect(has('key', obj)).to.be.true
15
15
 
16
- expect('null' in obj).toBe(true)
17
- expect(has('null', obj)).toBe(true)
16
+ expect('null' in obj).to.be.true
17
+ expect(has('null', obj)).to.be.true
18
18
 
19
19
  // @ts-expect-error
20
- expect(null in obj).toBe(true)
21
- expect(has(null, obj)).toBe(false)
20
+ expect(null in obj).to.be.true
21
+ expect(has(null, obj)).to.be.false
22
22
 
23
- expect('toString' in obj).toBe(true)
24
- expect(has('toString', obj)).toBe(false)
23
+ expect('toString' in obj).to.be.true
24
+ expect(has('toString', obj)).to.be.false
25
25
  },
26
26
 
27
- 'has: null': (/** @type {import('bun:test').Expect} */ expect) => {
27
+ 'has: null': (/** @type {import('chai').expect} */ expect) => {
28
28
  let typeError
29
29
 
30
30
  try {
@@ -35,6 +35,6 @@ export const tests = {
35
35
  }
36
36
 
37
37
  expect(typeError instanceof TypeError) // Cannot use 'in' operator to search for 'key' in null
38
- expect(has('key', null)).toBe(false)
38
+ expect(has('key', null)).to.be.false
39
39
  }
40
40
  }
package/is.d.ts CHANGED
@@ -12,6 +12,6 @@ export const is: {
12
12
  };
13
13
 
14
14
  export const tests: {
15
- is: (expect: import('bun:test').Expect) => void;
16
- 'is: toString.call': (expect: import('bun:test').Expect) => void;
15
+ is: (expect: Chai.ExpectStatic) => void;
16
+ 'is: toString.call': (expect: Chai.ExpectStatic) => void;
17
17
  };