feat: name export file with meaningful title instead of id (#379)

This commit is contained in:
kimatata
2026-01-10 19:31:47 +09:00
committed by GitHub
parent 475fa1ced7
commit ab348fc4e5
8 changed files with 71 additions and 8 deletions

View File

@@ -7,6 +7,7 @@ import defineStep from '../../models/steps.js';
import defineFolder from '../../models/folders.js';
import authMiddleware from '../../middleware/auth.js';
import visibilityMiddleware from '../../middleware/verifyVisible.js';
import { contentDisposition, toSafeFileName } from '../../config/contentDisposition.js';
import { testRunStatus, priorities, testTypes, automationStatus, templates } from '../../config/enums.js';
export default function (sequelize) {
@@ -31,6 +32,16 @@ export default function (sequelize) {
}
try {
const folder = await Folder.findByPk(folderId);
if (!folder) {
return res.status(404).send('Folder not found');
}
const folderName = toSafeFileName(folder.name);
const filename = `${folderName}.${type}`;
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
res.setHeader('Content-Disposition', contentDisposition(filename));
const cases = await Case.findAll({
attributes: { exclude: ['createdAt', 'updatedAt', 'caseSteps'] },
include: [
@@ -74,7 +85,6 @@ export default function (sequelize) {
});
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename=cases_folder_${folderId}.csv`);
return res.send(csv);
}

View File

@@ -34,7 +34,13 @@ vi.mock('../../middleware/verifyVisible.js', () => ({
}),
}));
// mock defineCase
const mockFolder = {
findByPk: vi.fn(),
};
vi.mock('../../models/folders.js', () => ({
default: () => mockFolder,
}));
const mockCase = {
findAll: vi.fn(),
belongsToMany: vi.fn(),
@@ -66,6 +72,8 @@ describe('GET /download with type=csv', () => {
});
it('should return CSV with human-readable labels for state, priority, type, automationStatus, and template', async () => {
mockFolder.findByPk.mockResolvedValue({ id: 1, name: 'Test Folder' });
mockCase.findAll.mockResolvedValue([
{
id: 1,
@@ -112,7 +120,7 @@ describe('GET /download with type=csv', () => {
expect(response.status).toBe(200);
expect(response.headers['content-type']).toBe('text/csv; charset=utf-8');
expect(response.headers['content-disposition']).toBe('attachment; filename=cases_folder_1.csv');
expect(response.headers['content-disposition']).toContain('attachment; filename="Test Folder.csv"');
const csvContent = response.text;

View File

@@ -10,6 +10,7 @@ import defineFolder from '../../models/folders.js';
import defineTag from '../../models/tags.js';
import authMiddleware from '../../middleware/auth.js';
import visibilityMiddleware from '../../middleware/verifyVisible.js';
import { contentDisposition, toSafeFileName } from '../../config/contentDisposition.js';
import { testRunCaseStatus, testRunStatus, priorities, testTypes, automationStatus } from '../../config/enums.js';
export default function (sequelize) {
@@ -40,6 +41,11 @@ export default function (sequelize) {
return res.status(404).send('Run not found');
}
const runName = toSafeFileName(run.name);
const filename = `${runName}.${type}`;
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
res.setHeader('Content-Disposition', contentDisposition(filename));
const runCases = await RunCase.findAll({
where: { runId },
include: [
@@ -107,7 +113,6 @@ export default function (sequelize) {
const xmlString = xml.end({ prettyPrint: true });
res.setHeader('Content-Type', 'application/xml');
res.setHeader('Content-Disposition', `attachment; filename=run_${runId}.xml`);
return res.send(xmlString);
} else if (type === 'json') {
return res.json(runCases);
@@ -129,7 +134,6 @@ export default function (sequelize) {
});
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename=run_${runId}.csv`);
return res.send(csv);
}

View File

@@ -156,7 +156,7 @@ describe('GET /download/:runId with type=csv', () => {
expect(response.status).toBe(200);
expect(response.headers['content-type']).toBe('text/csv; charset=utf-8');
expect(response.headers['content-disposition']).toBe('attachment; filename=run_1.csv');
expect(response.headers['content-disposition']).toContain('attachment; filename="Test Run.csv"');
const csvContent = response.text;