mirror of
https://github.com/liubinfighter/csv-lite.git
synced 2026-07-22 05:43:52 +00:00
fix: update .gitignore to include additional script and test file exclusions
feat: add text
This commit is contained in:
parent
978a4b0619
commit
0a1773ee96
8 changed files with 4007 additions and 1 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -20,5 +20,14 @@ data.json
|
||||||
./csv_metadata/
|
./csv_metadata/
|
||||||
csv_metadata
|
csv_metadata
|
||||||
|
|
||||||
|
# scripts for dev
|
||||||
|
scripts
|
||||||
|
|
||||||
|
# test
|
||||||
|
*.csv
|
||||||
|
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Test coverage reports
|
||||||
|
coverage/
|
||||||
|
|
|
||||||
23
jest.config.js
Normal file
23
jest.config.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/test'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest'
|
||||||
|
},
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/utils/csv-utils.ts'
|
||||||
|
],
|
||||||
|
moduleDirectories: ['node_modules', '<rootDir>/test/__mocks__'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^obsidian$': '<rootDir>/test/__mocks__/obsidian.js'
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: {
|
||||||
|
esModuleInterop: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
3814
package-lock.json
generated
3814
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,9 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|
@ -17,9 +19,12 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.11.6",
|
"@types/node": "^16.11.6",
|
||||||
"@types/papaparse": "^5.3.7",
|
"@types/papaparse": "^5.3.7",
|
||||||
|
"@types/jest": "^29.5.5",
|
||||||
"builtin-modules": "^3.3.0",
|
"builtin-modules": "^3.3.0",
|
||||||
"esbuild": "0.17.3",
|
"esbuild": "0.17.3",
|
||||||
|
"jest": "^29.7.0",
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
|
"ts-jest": "^29.1.1",
|
||||||
"tslib": "2.4.0",
|
"tslib": "2.4.0",
|
||||||
"typescript": "4.7.4"
|
"typescript": "4.7.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
test/__mocks__/obsidian.js
Normal file
3
test/__mocks__/obsidian.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
Notice: jest.fn().mockImplementation((message) => ({ message }))
|
||||||
|
};
|
||||||
143
test/csv-parser.test.ts
Normal file
143
test/csv-parser.test.ts
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { CSVUtils } from '../src/utils/csv-utils';
|
||||||
|
|
||||||
|
// Mock the i18n module
|
||||||
|
jest.mock('../src/i18n', () => ({
|
||||||
|
i18n: {
|
||||||
|
t: jest.fn((key: string) => key)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('CSV Parser Core Tests', () => {
|
||||||
|
describe('Basic CSV Parsing', () => {
|
||||||
|
test('should parse simple comma-separated values', () => {
|
||||||
|
const csvData = 'name,age,email\nJohn,25,john@example.com\nJane,30,jane@example.com';
|
||||||
|
const result = CSVUtils.parseCSV(csvData);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'age', 'email'],
|
||||||
|
['John', '25', 'john@example.com'],
|
||||||
|
['Jane', '30', 'jane@example.com']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle quoted fields with commas (main bug fix)', () => {
|
||||||
|
const csvData = 'name,description\n"John Doe","A person, with comma"\n"Jane","Normal description"';
|
||||||
|
const result = CSVUtils.parseCSV(csvData);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'description'],
|
||||||
|
['John Doe', 'A person, with comma'],
|
||||||
|
['Jane', 'Normal description']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle escaped quotes inside quoted fields', () => {
|
||||||
|
const csvData = 'name,description\n"Alice ""Wonder"" Land","Contains ""quotes"" inside"';
|
||||||
|
const result = CSVUtils.parseCSV(csvData);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'description'],
|
||||||
|
['Alice "Wonder" Land', 'Contains "quotes" inside']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Custom Delimiter Support', () => {
|
||||||
|
test('should handle semicolon delimiter', () => {
|
||||||
|
const csvData = 'name;age;email\nJohn;25;john@example.com';
|
||||||
|
const result = CSVUtils.parseCSV(csvData, { delimiter: ';' });
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'age', 'email'],
|
||||||
|
['John', '25', 'john@example.com']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle tab delimiter (TSV)', () => {
|
||||||
|
const csvData = 'name\tage\temail\nJohn\t25\tjohn@example.com';
|
||||||
|
const result = CSVUtils.parseCSV(csvData, { delimiter: '\t' });
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'age', 'email'],
|
||||||
|
['John', '25', 'john@example.com']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Edge Cases', () => {
|
||||||
|
test('should handle multiline content within quotes', () => {
|
||||||
|
const csvData = 'name,description\n"Charlie\nMulti-line","This description\nspans multiple lines"';
|
||||||
|
const result = CSVUtils.parseCSV(csvData);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'description'],
|
||||||
|
['Charlie\nMulti-line', 'This description\nspans multiple lines']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle empty fields', () => {
|
||||||
|
const csvData = 'name,age,email\nBob,,bob@mail.com\n,40,empty@name.com';
|
||||||
|
const result = CSVUtils.parseCSV(csvData);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'age', 'email'],
|
||||||
|
['Bob', '', 'bob@mail.com'],
|
||||||
|
['', '40', 'empty@name.com']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle complex test data from test-sample.csv', () => {
|
||||||
|
const complexCsvData = `name,age,email,description,notes
|
||||||
|
John Doe,25,john@example.com,Simple record,No special characters
|
||||||
|
"Jane Smith",30,"jane@test.com","Contains, comma","Normal quoted field"
|
||||||
|
"Alice ""Wonder"" Land",28,alice@wonder.com,"Contains ""quotes"" inside","Escaped quotes test"`;
|
||||||
|
|
||||||
|
const result = CSVUtils.parseCSV(complexCsvData);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'age', 'email', 'description', 'notes'],
|
||||||
|
['John Doe', '25', 'john@example.com', 'Simple record', 'No special characters'],
|
||||||
|
['Jane Smith', '30', 'jane@test.com', 'Contains, comma', 'Normal quoted field'],
|
||||||
|
['Alice "Wonder" Land', '28', 'alice@wonder.com', 'Contains "quotes" inside', 'Escaped quotes test']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Data Normalization', () => {
|
||||||
|
test('should normalize irregular table data', () => {
|
||||||
|
const data = [
|
||||||
|
['name', 'age', 'email'],
|
||||||
|
['John', '25'],
|
||||||
|
['Jane', '30', 'jane@example.com', 'extra']
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = CSVUtils.normalizeTableData(data);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
['name', 'age', 'email', ''],
|
||||||
|
['John', '25', '', ''],
|
||||||
|
['Jane', '30', 'jane@example.com', 'extra']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle empty input gracefully', () => {
|
||||||
|
expect(CSVUtils.normalizeTableData([])).toEqual([['']]);
|
||||||
|
expect(CSVUtils.normalizeTableData(null as any)).toEqual([['']]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CSV Generation', () => {
|
||||||
|
test('should convert 2D array back to CSV string', () => {
|
||||||
|
const data = [
|
||||||
|
['name', 'age', 'email'],
|
||||||
|
['John', '25', 'john@example.com'],
|
||||||
|
['Jane', '30', 'jane@example.com']
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = CSVUtils.unparseCSV(data);
|
||||||
|
const expected = 'name,age,email\nJohn,25,john@example.com\nJane,30,jane@example.com';
|
||||||
|
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
8
test/mocks/obsidian.ts
Normal file
8
test/mocks/obsidian.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
export class Notice {
|
||||||
|
constructor(public message: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出默认对象以确保兼容性
|
||||||
|
export default {
|
||||||
|
Notice
|
||||||
|
};
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"DOM",
|
"DOM",
|
||||||
"ES5",
|
"ES5",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue