admin管理员组文章数量:1023204
I am trying to write a unit test by using the Mock Service Worker (msw) to mock an exceljs
Passthrough Readable Stream, but I am getting this error:
FAIL __tests__/excel.test.js > getReadableStream > should return a readable stream for an HTTP URL
Error: Can't find end of central directory : is this a zip file ? If it is, see .html
❯ ZipEntries.readEndOfCentral node_modules/jszip/lib/zipEntries.js:167:23
❯ ZipEntries.load node_modules/jszip/lib/zipEntries.js:255:14
❯ node_modules/jszip/lib/load.js:48:24
❯ XLSX.load node_modules/exceljs/lib/xlsx/xlsx.js:279:17
❯ Module.readExcel helpers/excel.js:51:5
49|
50| const workbook = new ExcelJS.Workbook()
51| await workbook.xlsx.read(stream)
| ^
52|
53| return workbook
❯ __tests__/excel.test.js:95:19
excel.test.js
import { PassThrough } from 'node:stream'
import ExcelJS from 'exceljs'
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'
import { readExcel, getReadableStream } from '../excel.js'
import data from './__fixtures__/data.json'
const sheetName = 'Sheet1'
// Create a mock server
const server = setupServer()
// Start the server before all tests
beforeAll(() => server.listen())
// Close the server after all tests
afterAll(() => server.close())
// Reset handlers after each test
afterEach(() => server.resetHandlers())
export async function generateReadableStreamFromJson (jsonData) {
const workbook = new ExcelJS.Workbook()
const worksheet = workbook.addWorksheet(sheetName)
if (jsonData.length > 0) {
// Add header row
const header = Object.keys(jsonData[0])
worksheet.addRow(header)
// Add data rows
jsonData.forEach((data) => {
const rowValues = Object.values(data).map(value => value === null ? '' : value)
worksheet.addRow(rowValues)
})
} else {
// Add header row with empty data
worksheet.addRow([])
}
// Create a PassThrough stream
const stream = new PassThrough()
try {
// Write the workbook to the stream
await workbook.xlsx.write(stream)
stream.end()
} catch (error) {
stream.emit('error', error)
}
return stream
}
function validateExcelDataAgainstJson (excelData, jsonData) {
const [headerRow, ...dataRows] = excelData[0].data
// Transform Excel data into key-value objects
const excelObjects = dataRows.map((row) => {
const obj = {}
headerRow.slice(1).forEach((header, colIndex) => {
obj[header] = row[colIndex + 1]
})
return obj
})
// Compare the transformed Excel data against the JSON data
expect(excelObjects).toHaveLength(jsonData.length)
jsonData.forEach((jsonObj, index) => {
const excelObj = excelObjects[index]
Object.keys(jsonObj).forEach((key) => {
expect(excelObj[key]).toEqual(jsonObj[key] ?? '')
})
})
}
describe('getReadableStream', () => {
it('should return a readable stream for an HTTP URL', async () => {
const url = '.xlsx'
const readableStream = await generateReadableStreamFromJson(data)
server.use(
http.get(url, () => {
return new HttpResponse(readableStream, {
headers: {
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
})
})
)
const stream = await getReadableStream(url)
const excel = await readExcel(stream)
validateExcelDataAgainstJson(excel, data)
})
})
excel.js
import { URL } from 'node:url'
import { createReadStream } from 'node:fs'
import path from 'node:path'
import { Readable } from 'node:stream'
import ExcelJS from 'exceljs'
export async function getReadableStream (input) {
if (typeof input !== 'string' || input.trim() === '') {
throw new Error('Invalid input: Must be a non-empty string or a readable stream')
}
try {
const parsedUrl = new URL(input)
switch (parsedUrl.protocol) {
case 'http:':
case 'https:': {
const response = await fetch(input)
if (!response.ok) {
throw new Error(`Network request failed for ${input} with status code ${response.status}`)
}
if (!response.body || typeof response.body.pipe !== 'function') {
throw new Error(`Response body is not a readable stream for ${input}`)
}
return response.body
}
case 'file:':
return createReadStream(parsedUrl.pathname)
default:
throw new Error(`Unsupported protocol for URL: ${input}. Must use HTTP, HTTPS, or file protocol`)
}
} catch (e) {
// If the URL constructor throws an error or the protocol is unsupported, assume it's a local file path
console.warn(`Failed to parse URL: ${e.message}. Assuming local file path: ${input}`)
return createReadStream(path.resolve(input))
}
}
export async function readExcel (input) {
try {
let stream
if (typeof input === 'string') {
stream = await getReadableStream(input)
} else if (input instanceof Readable) {
stream = input
} else {
throw new Error('Invalid input: Must be a URL, file path, or readable stream')
}
const workbook = new ExcelJS.Workbook()
await workbook.xlsx.read(stream)
return workbook
} catch (e) {
console.error(`Failed to read Excel file from ${input}`, e)
throw e
}
}
package.json
{
"name": "excel-stream-test",
"description": "exceljs test",
"version": "1.0.0",
"license": "UNLICENSED",
"main": "index.js",
"type": "module",
"scripts": {
"test": "vitest run --passWithNoTests"
},
"engines": {
"node": ">=22.0.0"
},
"dependencies": {
"exceljs": "4.4.0"
},
"devDependencies": {
"@vitest/coverage-v8": "2.1.5",
"msw": "2.6.5",
"vite": "5.4.11",
"vitest": "2.1.5"
}
}
data.json
[
{
"field_1": "1"
},
{
"field_1": "2"
}
]
I am trying to write a unit test by using the Mock Service Worker (msw) to mock an exceljs
Passthrough Readable Stream, but I am getting this error:
FAIL __tests__/excel.test.js > getReadableStream > should return a readable stream for an HTTP URL
Error: Can't find end of central directory : is this a zip file ? If it is, see .html
❯ ZipEntries.readEndOfCentral node_modules/jszip/lib/zipEntries.js:167:23
❯ ZipEntries.load node_modules/jszip/lib/zipEntries.js:255:14
❯ node_modules/jszip/lib/load.js:48:24
❯ XLSX.load node_modules/exceljs/lib/xlsx/xlsx.js:279:17
❯ Module.readExcel helpers/excel.js:51:5
49|
50| const workbook = new ExcelJS.Workbook()
51| await workbook.xlsx.read(stream)
| ^
52|
53| return workbook
❯ __tests__/excel.test.js:95:19
excel.test.js
import { PassThrough } from 'node:stream'
import ExcelJS from 'exceljs'
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'
import { readExcel, getReadableStream } from '../excel.js'
import data from './__fixtures__/data.json'
const sheetName = 'Sheet1'
// Create a mock server
const server = setupServer()
// Start the server before all tests
beforeAll(() => server.listen())
// Close the server after all tests
afterAll(() => server.close())
// Reset handlers after each test
afterEach(() => server.resetHandlers())
export async function generateReadableStreamFromJson (jsonData) {
const workbook = new ExcelJS.Workbook()
const worksheet = workbook.addWorksheet(sheetName)
if (jsonData.length > 0) {
// Add header row
const header = Object.keys(jsonData[0])
worksheet.addRow(header)
// Add data rows
jsonData.forEach((data) => {
const rowValues = Object.values(data).map(value => value === null ? '' : value)
worksheet.addRow(rowValues)
})
} else {
// Add header row with empty data
worksheet.addRow([])
}
// Create a PassThrough stream
const stream = new PassThrough()
try {
// Write the workbook to the stream
await workbook.xlsx.write(stream)
stream.end()
} catch (error) {
stream.emit('error', error)
}
return stream
}
function validateExcelDataAgainstJson (excelData, jsonData) {
const [headerRow, ...dataRows] = excelData[0].data
// Transform Excel data into key-value objects
const excelObjects = dataRows.map((row) => {
const obj = {}
headerRow.slice(1).forEach((header, colIndex) => {
obj[header] = row[colIndex + 1]
})
return obj
})
// Compare the transformed Excel data against the JSON data
expect(excelObjects).toHaveLength(jsonData.length)
jsonData.forEach((jsonObj, index) => {
const excelObj = excelObjects[index]
Object.keys(jsonObj).forEach((key) => {
expect(excelObj[key]).toEqual(jsonObj[key] ?? '')
})
})
}
describe('getReadableStream', () => {
it('should return a readable stream for an HTTP URL', async () => {
const url = '.xlsx'
const readableStream = await generateReadableStreamFromJson(data)
server.use(
http.get(url, () => {
return new HttpResponse(readableStream, {
headers: {
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
})
})
)
const stream = await getReadableStream(url)
const excel = await readExcel(stream)
validateExcelDataAgainstJson(excel, data)
})
})
excel.js
import { URL } from 'node:url'
import { createReadStream } from 'node:fs'
import path from 'node:path'
import { Readable } from 'node:stream'
import ExcelJS from 'exceljs'
export async function getReadableStream (input) {
if (typeof input !== 'string' || input.trim() === '') {
throw new Error('Invalid input: Must be a non-empty string or a readable stream')
}
try {
const parsedUrl = new URL(input)
switch (parsedUrl.protocol) {
case 'http:':
case 'https:': {
const response = await fetch(input)
if (!response.ok) {
throw new Error(`Network request failed for ${input} with status code ${response.status}`)
}
if (!response.body || typeof response.body.pipe !== 'function') {
throw new Error(`Response body is not a readable stream for ${input}`)
}
return response.body
}
case 'file:':
return createReadStream(parsedUrl.pathname)
default:
throw new Error(`Unsupported protocol for URL: ${input}. Must use HTTP, HTTPS, or file protocol`)
}
} catch (e) {
// If the URL constructor throws an error or the protocol is unsupported, assume it's a local file path
console.warn(`Failed to parse URL: ${e.message}. Assuming local file path: ${input}`)
return createReadStream(path.resolve(input))
}
}
export async function readExcel (input) {
try {
let stream
if (typeof input === 'string') {
stream = await getReadableStream(input)
} else if (input instanceof Readable) {
stream = input
} else {
throw new Error('Invalid input: Must be a URL, file path, or readable stream')
}
const workbook = new ExcelJS.Workbook()
await workbook.xlsx.read(stream)
return workbook
} catch (e) {
console.error(`Failed to read Excel file from ${input}`, e)
throw e
}
}
package.json
{
"name": "excel-stream-test",
"description": "exceljs test",
"version": "1.0.0",
"license": "UNLICENSED",
"main": "index.js",
"type": "module",
"scripts": {
"test": "vitest run --passWithNoTests"
},
"engines": {
"node": ">=22.0.0"
},
"dependencies": {
"exceljs": "4.4.0"
},
"devDependencies": {
"@vitest/coverage-v8": "2.1.5",
"msw": "2.6.5",
"vite": "5.4.11",
"vitest": "2.1.5"
}
}
data.json
[
{
"field_1": "1"
},
{
"field_1": "2"
}
]
本文标签: nodejsMSW Mocking Readable StreamsStack Overflow
版权声明:本文标题:node.js - MSW Mocking Readable Streams - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745590411a2157850.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论