Skip to content

Commit 16b79bd

Browse files
committed
chore: wip
chore: wip
1 parent 0986b79 commit 16b79bd

File tree

4 files changed

+141
-9
lines changed

4 files changed

+141
-9
lines changed

storage/framework/core/spreadsheets/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
"scripts": {
4242
"build": "bun --bun build.ts",
4343
"typecheck": "bun --bun tsc --noEmit",
44-
"prepublishOnly": "bun run build"
44+
"prepublishOnly": "bun run build",
45+
"test": "bun test"
4546
},
4647
"devDependencies": {
4748
"@stacksjs/development": "workspace:*"

storage/framework/core/spreadsheets/src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,11 @@ export function generateCSVContent(content: Content): string {
150150
.map((row) =>
151151
row
152152
.map((cell) => {
153-
if (typeof cell === 'string' && cell.includes(',')) {
154-
return `"${cell}"`
153+
const cellString = String(cell)
154+
if (cellString.includes(',') || cellString.includes('"') || cellString.includes('\n')) {
155+
return `"${cellString.replace(/"/g, '""')}"`
155156
}
156-
return cell
157+
return cellString
157158
})
158159
.join(','),
159160
)

storage/framework/core/spreadsheets/tests/example.test.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { expect, describe, it, beforeEach, afterEach } from 'bun:test'
2+
import type { Content } from '../src/index'
3+
import { spreadsheet, createSpreadsheet } from '../src/index'
4+
import { existsSync, unlinkSync } from 'node:fs'
5+
6+
describe('Bun Spreadsheets', () => {
7+
let testData: Content
8+
9+
beforeEach(() => {
10+
testData = {
11+
headings: ['Name', 'Age', 'City'],
12+
data: [
13+
['John Doe', 30, 'New York'],
14+
['Jane Smith', 25, 'London'],
15+
['Bob Johnson', 35, 'Paris'],
16+
],
17+
}
18+
})
19+
20+
afterEach(() => {
21+
// Clean up any files created during tests
22+
const filesToDelete = ['output.csv', 'output.xlsx']
23+
filesToDelete.forEach((file) => {
24+
if (existsSync(file)) {
25+
unlinkSync(file)
26+
}
27+
})
28+
})
29+
30+
describe('Content Creation', () => {
31+
it('should create valid Content object', () => {
32+
expect(testData.headings.length).toBe(3)
33+
expect(testData.data.length).toBe(3)
34+
})
35+
36+
it('should handle empty data', () => {
37+
const emptyData: Content = { headings: [], data: [] }
38+
expect(() => createSpreadsheet(emptyData)).not.toThrow()
39+
})
40+
})
41+
42+
describe('CSV Generation', () => {
43+
it('should generate valid CSV content', async () => {
44+
const csvContent = spreadsheet(testData).csv().getContent() as string
45+
const lines = csvContent.split('\n')
46+
expect(lines[0]).toBe('Name,Age,City')
47+
expect(lines[1]).toBe('John Doe,30,New York')
48+
})
49+
50+
it('should handle special characters in CSV', () => {
51+
const specialData: Content = {
52+
headings: ['Name', 'Description'],
53+
data: [['John, Doe', 'Likes "quotes"']],
54+
}
55+
const csvContent = spreadsheet(specialData).csv().getContent() as string
56+
expect(csvContent).toBe('Name,Description\n"John, Doe","Likes ""quotes"""')
57+
})
58+
59+
// New test for handling numbers
60+
it('should correctly store numbers in CSV', () => {
61+
const numericData: Content = {
62+
headings: ['Name', 'Age', 'Score'],
63+
data: [
64+
['Alice', 28, 95.5],
65+
['Bob', 32, 88],
66+
['Charlie', 45, 72.75],
67+
],
68+
}
69+
const csvContent = spreadsheet(numericData).csv().getContent() as string
70+
const lines = csvContent.split('\n')
71+
expect(lines[0]).toBe('Name,Age,Score')
72+
expect(lines[1]).toBe('Alice,28,95.5')
73+
expect(lines[2]).toBe('Bob,32,88')
74+
expect(lines[3]).toBe('Charlie,45,72.75')
75+
})
76+
})
77+
78+
describe('Excel Generation', () => {
79+
it('should generate valid Excel content', () => {
80+
const excelContent = spreadsheet(testData).excel().getContent() as Uint8Array
81+
expect(excelContent).toBeInstanceOf(Uint8Array)
82+
})
83+
84+
// TODO: more excel tests
85+
})
86+
87+
describe('File Storage', () => {
88+
it('should store CSV file', async () => {
89+
await spreadsheet(testData).store('output.csv')
90+
expect(existsSync('output.csv')).toBe(true)
91+
})
92+
93+
it('should store Excel file', async () => {
94+
await spreadsheet(testData).store('output.xlsx')
95+
expect(existsSync('output.xlsx')).toBe(true)
96+
})
97+
})
98+
99+
describe('Download Response', () => {
100+
it('should create valid download response for CSV', () => {
101+
const response = spreadsheet(testData).csv().download('test.csv')
102+
expect(response).toBeInstanceOf(Response)
103+
expect(response.headers.get('Content-Type')).toBe('text/csv')
104+
})
105+
106+
it('should create valid download response for Excel', () => {
107+
const response = spreadsheet(testData).excel().download('test.xlsx')
108+
expect(response).toBeInstanceOf(Response)
109+
expect(response.headers.get('Content-Type')).toBe(
110+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
111+
)
112+
})
113+
})
114+
115+
describe('Method Chaining', () => {
116+
it('should support method chaining', async () => {
117+
try {
118+
await spreadsheet(testData).csv().store('output.csv')
119+
// If we reach here, no error was thrown
120+
expect(true).toBe(true)
121+
} catch (error) {
122+
console.error('Error in method chaining:', error)
123+
// Fail the test if an error is caught
124+
expect(error).toBeUndefined()
125+
}
126+
})
127+
})
128+
129+
describe('Error Handling', () => {
130+
it('should throw error for unsupported spreadsheet type', () => {
131+
// @ts-expect-error: Testing invalid type
132+
expect(() => createSpreadsheet(testData, { type: 'pdf' })).toThrow()
133+
})
134+
})
135+
})

0 commit comments

Comments
 (0)