Skip to content

first pass at using jest for testing #664

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

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
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
26 changes: 26 additions & 0 deletions src/danfojs-node/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @ts-check
/** @type {import('jest').Config} */

const config = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/test/**/*.test.ts'],
moduleFileExtensions: ['ts', 'js'],
transform: {
'^.+\\.ts$': ['ts-jest', {
tsconfig: 'tsconfig.json'
}]
},
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json'
}
},
coverageDirectory: 'coverage',
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts'
]
};

module.exports = config;
51 changes: 20 additions & 31 deletions src/danfojs-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,24 @@
"dist/"
],
"dependencies": {
"@tensorflow/tfjs-node": "^3.13.0",
"mathjs": "^9.4.4",
"node-fetch": "^2.6.1",
"papaparse": "^5.3.1",
"@tensorflow/tfjs-node": "^4.22.0",
"mathjs": "^14.4.0",
"node-fetch": "2.6.7",
"papaparse": "^5.5.2",
"request": "^2.88.2",
"stream-json": "^1.7.3",
"table": "6.7.1",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz"
"stream-json": "^1.9.1",
"table": "6.9.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
},
"scripts": {
"test": "nyc mocha --require ts-node/register test/**/*.test.ts",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:clean": "yarn build:clean && yarn test",
"dev": "nodemon",
"build": "tsc",
"build:clean": "rm -rf ./dist && node ./scripts/prebuild.js && tsc",
"lint": "eslint ./src",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls",
"coverage": "nyc report --reporter=text-lcov | coveralls && nyc report --reporter=lcov",
"patch": "npm version patch"
},
"publishConfig": {
Expand Down Expand Up @@ -67,33 +67,22 @@
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.10.4",
"@babel/register": "^7.10.1",
"@types/chai": "^4.2.19",
"@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^9.0.0",
"@types/node": "^16.9.6",
"@types/papaparse": "^5.2.6",
"@types/request": "^2.48.7",
"@types/stream-json": "^1.7.1",
"@jest/globals": "^29.7.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.0",
"@types/papaparse": "^5.3.15",
"@types/request": "^2.48.12",
"@types/stream-json": "^1.7.8",
"@types/table": "^6.3.2",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.1.0",
"dotenv": "^10.0.0",
"dts-bundle-generator": "^5.9.0",
"eslint": "^7.1.0",
"mocha": "^7.2.0",
"jest": "^29.7.0",
"nodemon": "^2.0.7",
"nyc": "^15.1.0",
"rimraf": "^3.0.2",
"ts-mocha": "^10.0.0",
"ts-node": "^10.0.0",
"typescript": "^4.4.2",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.8.2",
"yarn": "^1.22.10"
},
"nyc": {
"reporter": [
"lcov",
"text"
]
}
}
153 changes: 52 additions & 101 deletions src/danfojs-node/test/io/csv.reader.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import path from "path";
import chai, { assert, expect } from "chai";
import { describe, it } from "mocha";
import chaiAsPromised from "chai-as-promised";
import { DataFrame, readCSV, Series, streamCSV, toCSV, toJSON } from "../../dist/danfojs-node/src";
import fs from 'fs';
import process from 'process';

chai.use(chaiAsPromised);

describe("readCSV", function () {
this.timeout(10000);
// Add Jest types for TypeScript support
import { describe, expect, it } from '@jest/globals';

describe("readCSV", () => {
const testSamplesDir = path.join(process.cwd(), "test", "samples");

it("Read local csv file works", async function () {
it("Read local csv file works", async () => {
const filePath = path.join(testSamplesDir, "titanic.csv");
let df = await readCSV(filePath, { header: true, preview: 5 });
assert.deepEqual(df.shape, [5, 8]);
assert.deepEqual(df.columns, [
expect(df.shape).toEqual([5, 8]);
expect(df.columns).toEqual([
'Survived',
'Pclass',
'Name',
Expand All @@ -27,15 +23,15 @@ describe("readCSV", function () {
'Parents/Children Aboard',
'Fare'
]);
assert.deepEqual(df.dtypes, [
expect(df.dtypes).toEqual([
'int32', 'int32',
'string', 'string',
'int32', 'int32',
'int32', 'float32'
]);
});

it("Read local CSV file with config works", async function () {
it("Read local CSV file with config works", async () => {
const filePath = path.join(testSamplesDir, "titanic.csv");
const frameConfig = {
columns: [
Expand All @@ -50,8 +46,8 @@ describe("readCSV", function () {
]
};
let df = await readCSV(filePath, { frameConfig, header: true, preview: 5 });
assert.deepEqual(df.shape, [5, 8]);
assert.deepEqual(df.columns, [
expect(df.shape).toEqual([5, 8]);
expect(df.columns).toEqual([
'A',
'B',
'C',
Expand All @@ -61,15 +57,15 @@ describe("readCSV", function () {
'G',
'H'
]);
assert.deepEqual(df.dtypes, [
expect(df.dtypes).toEqual([
'int32', 'int32',
'string', 'string',
'int32', 'int32',
'int32', 'float32'
]);
});

it("Read local csv with correct types and format works", async function () {
it("Read local csv with correct types and format works", async () => {
const filePath = path.join(testSamplesDir, "iris.csv");
let df = await readCSV(filePath, { header: true, preview: 5 });
const values = [
Expand All @@ -79,105 +75,50 @@ describe("readCSV", function () {
[4.6, 3.1, 1.5, 0.2, 0.0],
[5.0, 3.6, 1.4, 0.2, 0.0]
];
assert.deepEqual(df.values, values);
expect(df.values).toEqual(values);
});

it("Throws error if file not found", async function () {
it("Throws error if file not found", async () => {
const filePath = "notfound.csv";
await expect(readCSV(filePath)).to.be.rejectedWith("ENOENT: no such file or directory");
await expect(readCSV(filePath)).rejects.toThrow("ENOENT: no such file or directory");
});

it("Throws error if file not found over http", async function () {
it("Throws error if file not found over http", async () => {
const filePath = "https://getdata.com/notfound.csv";
await expect(readCSV(filePath)).to.be.rejectedWith(/HTTP \d+:/);
await expect(readCSV(filePath)).rejects.toThrow(/HTTP \d+:/);
});

it("Throws error when reading empty CSV file", async function () {
it("Throws error when reading empty CSV file", async () => {
const filePath = path.join(testSamplesDir, "empty.csv");
// Create empty file
fs.writeFileSync(filePath, "");
await expect(readCSV(filePath)).to.be.rejectedWith("No data found in CSV file");
await expect(readCSV(filePath)).rejects.toThrow("No data found in CSV file");
fs.unlinkSync(filePath); // Clean up
});

it("Throws error when reading malformed CSV", async function () {
it("Throws error when reading malformed CSV", async () => {
const filePath = path.join(testSamplesDir, "malformed.csv");
// Create malformed CSV file
fs.writeFileSync(filePath, "a,b,c\n1,2\n3,4,5,6");
await expect(readCSV(filePath)).to.be.rejectedWith("CSV parsing errors");
await expect(readCSV(filePath)).rejects.toThrow("CSV parsing errors");
fs.unlinkSync(filePath); // Clean up
});

it("Throws error when DataFrame creation fails", async function () {
it("Throws error when DataFrame creation fails", async () => {
const filePath = path.join(testSamplesDir, "invalid.csv");
await expect(readCSV(filePath)).to.be.rejectedWith("ENOENT: no such file or directory");
});

it("Preserves leading zeros when dtype is string", async function () {
const filePath = path.join(testSamplesDir, "leading_zeros.csv");
// Create test CSV file
fs.writeFileSync(filePath, "codes\n012345\n001234");

try {
const df = await readCSV(filePath, {
frameConfig: {
dtypes: ["string"]
}
});

assert.deepEqual(df.values, [["012345"], ["001234"]]);
assert.deepEqual(df.dtypes, ["string"]);

// Verify the values are actually strings
const jsonData = toJSON(df);
assert.deepEqual(jsonData, [{ codes: "012345" }, { codes: "001234" }]);

// Clean up
fs.unlinkSync(filePath);
} catch (error) {
// Clean up even if test fails
fs.unlinkSync(filePath);
throw error;
}
});

it("Converts to numbers when dtype is not string", async function () {
const filePath = path.join(testSamplesDir, "leading_zeros.csv");
// Create test CSV file
fs.writeFileSync(filePath, "codes\n012345\n001234");

try {
const df = await readCSV(filePath); // default behavior without string dtype

// Values should be converted to numbers
assert.deepEqual(df.values, [[12345], [1234]]);
assert.deepEqual(df.dtypes, ["int32"]);

// Verify JSON output
const jsonData = toJSON(df);
assert.deepEqual(jsonData, [{ codes: 12345 }, { codes: 1234 }]);

// Clean up
fs.unlinkSync(filePath);
} catch (error) {
// Clean up even if test fails
fs.unlinkSync(filePath);
throw error;
}
await expect(readCSV(filePath)).rejects.toThrow("ENOENT: no such file or directory");
});
});

describe("streamCSV", function () {
this.timeout(100000);

describe("streamCSV", () => {
const testSamplesDir = path.join(process.cwd(), "test", "samples");

it("Streaming local csv file with callback works", async function () {
it("Streaming local csv file with callback works", async () => {
const filePath = path.join(testSamplesDir, "titanic.csv");
await streamCSV(filePath, (df) => {
if (df) {
assert.deepEqual(df.shape, [1, 8]);
assert.deepEqual(df.columns, [
expect(df.shape).toEqual([1, 8]);
expect(df.columns).toEqual([
'Survived',
'Pclass',
'Name',
Expand All @@ -188,58 +129,68 @@ describe("streamCSV", function () {
'Fare'
]);
} else {
assert.deepEqual(df, null);
expect(df).toBeNull();
}
}, { header: true });
});

it("Throws error when streaming non-existent file", async function () {
it("Throws error when streaming non-existent file", async () => {
const filePath = "notfound.csv";
await expect(streamCSV(filePath, () => {})).to.be.rejectedWith("ENOENT: no such file or directory");
await expect(streamCSV(filePath, () => {})).rejects.toThrow("ENOENT: no such file or directory");
});

it("Throws error when streaming malformed CSV", async function () {
it("Throws error when streaming malformed CSV", async () => {
const filePath = path.join(testSamplesDir, "malformed_stream.csv");
// Create malformed CSV file
fs.writeFileSync(filePath, "a,b,c\n1,2\n3,4,5,6");
await expect(streamCSV(filePath, () => {})).to.be.rejectedWith("CSV parsing errors");
await expect(streamCSV(filePath, () => {})).rejects.toThrow("CSV parsing errors");
fs.unlinkSync(filePath); // Clean up
});
});

describe("toCSV", function () {
describe("toCSV", () => {
const testSamplesDir = path.join(process.cwd(), "test", "samples");

it("toCSV works", async function () {
it("toCSV works", async () => {
const data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
let df = new DataFrame(data, { columns: ["a", "b", "c", "d"] });
assert.deepEqual(toCSV(df, {}), `a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11,12\n`);
expect(toCSV(df, {})).toBe(`a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11,12\n`);
});

it("toCSV works for specified separator", async function () {
it("toCSV works for specified separator", async () => {
const data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
let df = new DataFrame(data, { columns: ["a", "b", "c", "d"] });
assert.deepEqual(toCSV(df, { sep: "+" }), `a+b+c+d\n1+2+3+4\n5+6+7+8\n9+10+11+12\n`);
expect(toCSV(df, { sep: "+" })).toBe(`a+b+c+d\n1+2+3+4\n5+6+7+8\n9+10+11+12\n`);
});

it("toCSV write to local file works", async function () {
it("toCSV write to local file works", async () => {
const data = [[1, 2, 3, "4"], [5, 6, 7, "8"], [9, 10, 11, "12"]];
let df = new DataFrame(data, { columns: ["a", "b", "c", "d"] });
const filePath = path.join(testSamplesDir, "test_write.csv");

// Write file
toCSV(df, { sep: ",", filePath });

// Verify file was written
expect(fs.existsSync(filePath)).toBe(true);

// Read and verify contents
const fileContent = fs.readFileSync(filePath, 'utf-8');
expect(fileContent).toBe(`a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11,12\n`);

// Clean up
fs.unlinkSync(filePath);
});

it("toCSV works for series", async function () {
it("toCSV works for series", async () => {
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let df = new Series(data);
assert.deepEqual(toCSV(df, { sep: "+" }), `1+2+3+4+5+6+7+8+9+10+11+12`);
expect(toCSV(df, { sep: "+" })).toBe(`1+2+3+4+5+6+7+8+9+10+11+12`);
});

it("calling df.toCSV works", async function () {
it("calling df.toCSV works", async () => {
const data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
let df = new DataFrame(data, { columns: ["a", "b", "c", "d"] });
assert.deepEqual(df.toCSV(), `a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11,12\n`);
expect(df.toCSV()).toBe(`a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11,12\n`);
});
});
Loading
Loading