126 lines
4.8 KiB
JavaScript
126 lines
4.8 KiB
JavaScript
import { randomUUID } from 'node:crypto';
|
|
import argon2 from 'argon2';
|
|
import ExcelJS from 'exceljs';
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
import { createServer } from '../src/app.js';
|
|
import { db } from '../src/db/knex.js';
|
|
import { seedSurveyDefinition } from '../src/survey/seed.js';
|
|
import { truncateTables } from './truncate.js';
|
|
|
|
const email = 'export-admin@example.com';
|
|
const password = 'correct-password';
|
|
|
|
/**
|
|
* @param {import('@hapi/hapi').ServerInjectResponse} response
|
|
*/
|
|
function sessionCookie(response) {
|
|
const raw = response.headers['set-cookie'];
|
|
const header = Array.isArray(raw) ? raw[0] : raw;
|
|
return header.split(';')[0];
|
|
}
|
|
|
|
describe('survey export', () => {
|
|
/** @type {import('@hapi/hapi').Server | undefined} */
|
|
let server;
|
|
|
|
beforeEach(async () => {
|
|
await seedSurveyDefinition();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await server?.stop();
|
|
await truncateTables();
|
|
});
|
|
|
|
it('writes response headers, question ids, and choice counts', async () => {
|
|
server = await createServer();
|
|
await db('admin_users').insert({
|
|
name: 'Administrator',
|
|
email,
|
|
password_hash: await argon2.hash(password, { type: argon2.argon2id }),
|
|
is_active: true,
|
|
});
|
|
const login = await server.inject({
|
|
method: 'POST',
|
|
url: '/api/admin/auth/login',
|
|
payload: { email, password },
|
|
});
|
|
const definitionId = await seedSurveyDefinition();
|
|
const [applicationId] = await db('applications').insert({
|
|
public_id: randomUUID(),
|
|
email: 'physician@example.com',
|
|
status: 'APPROVED',
|
|
full_name: 'Asha Rao',
|
|
registration_no: 'MMC-100',
|
|
version: 1,
|
|
});
|
|
await db('survey_responses').insert({
|
|
application_id: applicationId,
|
|
survey_definition_id: definitionId,
|
|
answers: JSON.stringify({
|
|
name: 'Dr Asha Rao',
|
|
adherence_comparison: { choice: 'much_better' },
|
|
barriers: { choices: ['cost', 'other'], other: 'Lack of time' },
|
|
benefit: { choices: ['elderly'] },
|
|
pack_split: {
|
|
regular_pmdi: { asthma: 25, copd: 25 },
|
|
regular_rotacap: { asthma: 25, copd: 25 },
|
|
extended_pmdi: { asthma: 25, copd: 25 },
|
|
extended_rotacap: { asthma: 25, copd: 25 },
|
|
},
|
|
}),
|
|
status: 'COMPLETED',
|
|
started_at: new Date(),
|
|
completed_at: new Date(),
|
|
});
|
|
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: '/api/admin/survey/export?status=COMPLETED',
|
|
headers: { cookie: sessionCookie(login) },
|
|
});
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
expect(response.headers['content-disposition']).toContain('survey-responses.xlsx');
|
|
const workbook = new ExcelJS.Workbook();
|
|
await workbook.xlsx.load(response.rawPayload);
|
|
const responses = workbook.getWorksheet('Responses');
|
|
const headings = responses.getRow(1).values.slice(1);
|
|
const optionLabels = responses.getRow(2).values;
|
|
const data = responses.getRow(3);
|
|
expect(headings).toContain(
|
|
'Prescribing % - Extended-Dose Pack — Rotacap (pack of 60) - COPD',
|
|
);
|
|
const column =
|
|
headings.indexOf('Prescribing % - Extended-Dose Pack — Rotacap (pack of 60) - COPD') + 1;
|
|
expect(data.getCell(column).value).toBe(25);
|
|
expect(data.getCell(headings.indexOf('Physician name') + 1).value).toBe('Asha Rao');
|
|
|
|
const barriersHeading =
|
|
'Reasons for Non-adherence: In your opinion, what are the key barriers to adherence to inhaled maintenance therapy of formoterol/budesonide in your asthma and COPD patients?';
|
|
const barriersStart = headings.indexOf(barriersHeading) + 1;
|
|
expect(barriersStart).toBeGreaterThan(0);
|
|
expect(responses.getCell(1, barriersStart + 1).isMerged).toBe(true);
|
|
expect(optionLabels[barriersStart]).toBe('Side effects');
|
|
const barrierCell = (/** @type {string} */ label) =>
|
|
data.getCell(optionLabels.indexOf(label, barriersStart)).value;
|
|
expect(barrierCell('Cost')).toBe('Yes');
|
|
expect(barrierCell('Side effects')).toBeNull();
|
|
expect(barrierCell('Other (specify)')).toBe('Lack of time');
|
|
|
|
const elderly = optionLabels.indexOf('Elderly patients');
|
|
expect(responses.getCell(1, elderly).value).toBe(
|
|
'Patient Characteristics Associated with Adherence: Which patients, in your opinion, are most likely to benefit from an Extended-dose pack?',
|
|
);
|
|
expect(data.getCell(elderly).value).toBe('Yes');
|
|
const questions = workbook.getWorksheet('Questions');
|
|
const questionIds = questions.getColumn(1).values;
|
|
expect(questionIds).toContain('pack_split');
|
|
const summary = workbook.getWorksheet('Summary');
|
|
const summaryText = summary.getSheetValues().flat().join(' ');
|
|
expect(summaryText).toContain('Much better');
|
|
expect(summaryText).toContain('1');
|
|
expect(await db('audit_log').where({ action: 'SURVEY_EXPORTED' }).first()).toBeTruthy();
|
|
});
|
|
});
|