Skip to content

Commit a7d40f4

Browse files
Iteration 258: add pd.testing (assertSeriesEqual/assertFrameEqual/assertIndexEqual)
Implements the pd.testing module with: - assertSeriesEqual: deep equality check with dtype, index, name, NaN, and tolerance options - assertFrameEqual: per-column deep equality with checkLike (column-order insensitive) mode - assertIndexEqual: label-by-label Index comparison - AssertionError: custom Error subclass with detailed diff messages Run: https://github.com/githubnext/tsessebe/actions/runs/24814455663 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8d2e375 commit a7d40f4

6 files changed

Lines changed: 1005 additions & 0 deletions

File tree

playground/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,11 @@ <h3><a href="infer_objects.html" style="color: var(--accent); text-decoration: n
444444
<p>inferObjectsSeries / inferObjectsDataFrame / convertDtypesSeries / convertDtypesDataFrame — promote object-typed Series to better dtypes and parse string columns as numbers. Mirrors pandas infer_objects() and convert_dtypes().</p>
445445
<div class="status done">✅ Complete</div>
446446
</div>
447+
<div class="feature-card">
448+
<h3><a href="testing.html" style="color: var(--accent); text-decoration: none;">🧪 testing — Assertion Utilities</a></h3>
449+
<p>assertSeriesEqual / assertFrameEqual / assertIndexEqual — rich assertion helpers for use in test suites. Numeric tolerance, checkLike column-order mode, dtype checks, AssertionError with detailed diff messages. Mirrors pandas.testing.</p>
450+
<div class="status done">✅ Complete</div>
451+
</div>
447452
</section>
448453
<div class="features-grid">
449454
<div class="feature-card">

playground/testing.html

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>tsb — testing utilities</title>
7+
<style>
8+
* { box-sizing: border-box; margin: 0; padding: 0; }
9+
body { font-family: system-ui, sans-serif; line-height: 1.6; background: #f8f9fa; color: #212529; }
10+
header { background: #0d6efd; color: #fff; padding: 1.5rem 2rem; }
11+
header h1 { font-size: 1.8rem; }
12+
header p { opacity: 0.85; margin-top: 0.25rem; }
13+
main { max-width: 900px; margin: 2rem auto; padding: 0 1.5rem; }
14+
section { background: #fff; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,.08); margin-bottom: 2rem; padding: 1.5rem; }
15+
h2 { font-size: 1.25rem; margin-bottom: 1rem; color: #0d6efd; border-bottom: 1px solid #e9ecef; padding-bottom: 0.5rem; }
16+
h3 { font-size: 1rem; margin: 1rem 0 0.5rem; color: #495057; }
17+
pre { background: #1e1e1e; color: #d4d4d4; padding: 1rem; border-radius: 6px; overflow-x: auto; font-size: 0.85rem; }
18+
code { font-family: "Cascadia Code", "Fira Code", monospace; }
19+
.pass { color: #198754; font-weight: bold; }
20+
.fail { color: #dc3545; font-weight: bold; }
21+
.note { background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 0.75rem 1rem; font-size: 0.9rem; }
22+
table { border-collapse: collapse; width: 100%; font-size: 0.9rem; }
23+
th, td { border: 1px solid #dee2e6; padding: 0.4rem 0.7rem; text-align: left; }
24+
th { background: #f1f3f5; font-weight: 600; }
25+
a { color: #0d6efd; }
26+
</style>
27+
</head>
28+
<body>
29+
<header>
30+
<h1>tsb — <code>testing</code> utilities</h1>
31+
<p>assertSeriesEqual · assertFrameEqual · assertIndexEqual · mirrors <code>pandas.testing</code></p>
32+
</header>
33+
34+
<main>
35+
36+
<section>
37+
<h2>Overview</h2>
38+
<p>
39+
The <strong>tsb testing</strong> module provides assertion helpers for comparing tsb objects
40+
in test suites — analogous to <code>pandas.testing.assert_series_equal</code>,
41+
<code>assert_frame_equal</code>, and <code>assert_index_equal</code>.
42+
</p>
43+
<p style="margin-top:0.75rem">
44+
When a check fails, a descriptive <code>AssertionError</code> is thrown with information about
45+
which element differed and at which position — making test failures easy to diagnose.
46+
</p>
47+
</section>
48+
49+
<section>
50+
<h2>Import</h2>
51+
<pre><code>import {
52+
assertSeriesEqual,
53+
assertFrameEqual,
54+
assertIndexEqual,
55+
AssertionError,
56+
} from "tsb";</code></pre>
57+
</section>
58+
59+
<section>
60+
<h2><code>assertSeriesEqual(left, right, options?)</code></h2>
61+
<p>Assert that two Series contain identical values (with optional tolerance for floats).</p>
62+
<h3>Passing example</h3>
63+
<pre><code>import { Series, assertSeriesEqual } from "tsb";
64+
65+
const a = new Series({ data: [1, 2, 3], name: "x" });
66+
const b = new Series({ data: [1, 2, 3], name: "x" });
67+
assertSeriesEqual(a, b);
68+
// ✅ no exception thrown</code></pre>
69+
70+
<h3>Failing example</h3>
71+
<pre><code>const c = new Series({ data: [1, 2, 99], name: "x" });
72+
assertSeriesEqual(a, c);
73+
// ❌ AssertionError: Series: values differ at index 2 (position 2).
74+
// left=3, right=99</code></pre>
75+
76+
<h3>Float tolerance</h3>
77+
<pre><code>const p = new Series({ data: [1.0, 2.0] });
78+
const q = new Series({ data: [1.0 + 1e-9, 2.0] }); // tiny rounding error
79+
assertSeriesEqual(p, q); // ✅ passes (within default atol=1e-8)
80+
81+
assertSeriesEqual(p, q, { checkExact: true }); // ❌ exact comparison fails</code></pre>
82+
83+
<h3>Options</h3>
84+
<table>
85+
<tr><th>Option</th><th>Type</th><th>Default</th><th>Description</th></tr>
86+
<tr><td><code>checkDtypes</code></td><td>boolean</td><td>true</td><td>Compare dtype of both Series</td></tr>
87+
<tr><td><code>checkIndex</code></td><td>boolean</td><td>true</td><td>Compare row index labels</td></tr>
88+
<tr><td><code>checkNames</code></td><td>boolean</td><td>true</td><td>Compare Series name and index name</td></tr>
89+
<tr><td><code>checkExact</code></td><td>boolean</td><td>false</td><td>Exact numeric equality (no tolerance)</td></tr>
90+
<tr><td><code>rtol</code></td><td>number</td><td>1e-5</td><td>Relative tolerance</td></tr>
91+
<tr><td><code>atol</code></td><td>number</td><td>1e-8</td><td>Absolute tolerance</td></tr>
92+
<tr><td><code>objLabel</code></td><td>string</td><td>"Series"</td><td>Error message prefix</td></tr>
93+
</table>
94+
</section>
95+
96+
<section>
97+
<h2><code>assertFrameEqual(left, right, options?)</code></h2>
98+
<p>Assert that two DataFrames are structurally and value-identical.</p>
99+
100+
<h3>Passing example</h3>
101+
<pre><code>import { DataFrame, assertFrameEqual } from "tsb";
102+
103+
const a = DataFrame.fromColumns({ x: [1, 2], y: [3, 4] });
104+
const b = DataFrame.fromColumns({ x: [1, 2], y: [3, 4] });
105+
assertFrameEqual(a, b); // ✅</code></pre>
106+
107+
<h3>Ignore column order</h3>
108+
<pre><code>const c = DataFrame.fromColumns({ y: [3, 4], x: [1, 2] }); // columns reversed
109+
assertFrameEqual(a, c, { checkLike: true }); // ✅ order ignored</code></pre>
110+
111+
<h3>Options</h3>
112+
<table>
113+
<tr><th>Option</th><th>Type</th><th>Default</th><th>Description</th></tr>
114+
<tr><td><code>checkDtypes</code></td><td>boolean</td><td>true</td><td>Compare column dtypes</td></tr>
115+
<tr><td><code>checkIndex</code></td><td>boolean</td><td>true</td><td>Compare row index labels</td></tr>
116+
<tr><td><code>checkNames</code></td><td>boolean</td><td>true</td><td>Compare index and column names</td></tr>
117+
<tr><td><code>checkLike</code></td><td>boolean</td><td>false</td><td>Ignore column order</td></tr>
118+
<tr><td><code>checkExact</code></td><td>boolean</td><td>false</td><td>Exact numeric equality</td></tr>
119+
<tr><td><code>rtol</code></td><td>number</td><td>1e-5</td><td>Relative tolerance</td></tr>
120+
<tr><td><code>atol</code></td><td>number</td><td>1e-8</td><td>Absolute tolerance</td></tr>
121+
<tr><td><code>objLabel</code></td><td>string</td><td>"DataFrame"</td><td>Error message prefix</td></tr>
122+
</table>
123+
</section>
124+
125+
<section>
126+
<h2><code>assertIndexEqual(left, right, options?)</code></h2>
127+
<p>Assert that two Index objects have identical labels.</p>
128+
<pre><code>import { Index, assertIndexEqual } from "tsb";
129+
130+
const a = new Index(["a", "b", "c"]);
131+
const b = new Index(["a", "b", "c"]);
132+
assertIndexEqual(a, b); // ✅
133+
134+
const c = new Index(["a", "b", "z"]);
135+
assertIndexEqual(a, c);
136+
// ❌ AssertionError: Index: Index values differ at position 2. left=c, right=z</code></pre>
137+
</section>
138+
139+
<section>
140+
<h2><code>AssertionError</code></h2>
141+
<p>
142+
All failed assertions throw an <code>AssertionError</code> instance (extends <code>Error</code>).
143+
It can be caught explicitly or used with <code>expect().toThrow(AssertionError)</code> in bun:test.
144+
</p>
145+
<pre><code>import { AssertionError, assertSeriesEqual, Series } from "tsb";
146+
147+
try {
148+
assertSeriesEqual(
149+
new Series({ data: [1, 2, 3] }),
150+
new Series({ data: [1, 2, 4] }),
151+
);
152+
} catch (e) {
153+
if (e instanceof AssertionError) {
154+
console.error("Assertion failed:", e.message);
155+
}
156+
}</code></pre>
157+
<p class="note" style="margin-top:0.75rem">
158+
💡 In <code>bun:test</code>, use <code>expect(() =&gt; assertSeriesEqual(a, b)).toThrow(AssertionError)</code>
159+
to write negative assertions.
160+
</p>
161+
</section>
162+
163+
<section>
164+
<h2>pandas equivalents</h2>
165+
<table>
166+
<tr><th>tsb</th><th>pandas</th></tr>
167+
<tr><td><code>assertSeriesEqual(a, b)</code></td><td><code>pd.testing.assert_series_equal(a, b)</code></td></tr>
168+
<tr><td><code>assertFrameEqual(a, b)</code></td><td><code>pd.testing.assert_frame_equal(a, b)</code></td></tr>
169+
<tr><td><code>assertIndexEqual(a, b)</code></td><td><code>pd.testing.assert_index_equal(a, b)</code></td></tr>
170+
<tr><td><code>AssertionError</code></td><td><code>AssertionError</code> (Python built-in)</td></tr>
171+
</table>
172+
</section>
173+
174+
</main>
175+
</body>
176+
</html>

src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,3 +652,14 @@ export type {
652652
ResampleAggFn,
653653
ResampleOptions,
654654
} from "./stats/index.ts";
655+
export {
656+
AssertionError,
657+
assertSeriesEqual,
658+
assertFrameEqual,
659+
assertIndexEqual,
660+
} from "./testing/index.ts";
661+
export type {
662+
AssertSeriesEqualOptions,
663+
AssertFrameEqualOptions,
664+
AssertIndexEqualOptions,
665+
} from "./testing/index.ts";

src/testing/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export {
2+
AssertionError,
3+
assertSeriesEqual,
4+
assertFrameEqual,
5+
assertIndexEqual,
6+
} from "./testing.ts";
7+
export type {
8+
AssertSeriesEqualOptions,
9+
AssertFrameEqualOptions,
10+
AssertIndexEqualOptions,
11+
} from "./testing.ts";

0 commit comments

Comments
 (0)