- Export modules (export-csv/json/xlsx) mirroring the PDF layout, plus a Next Up import CSV matching the raycast/nextup column contract. - Hybrid Year Level: View Grades history (exact per-semester standing), falling back to units math (prospectus total - lacking), then profile. Popup adds a year-mode setting (auto/custom/off). - On-page button row now offers PDF / CSV / Excel / JSON / Next Up. - Bump version to 1.4.0. - Tag-only build workflow with an optional AMO sign step for a signed .xpi; separate ci.yml keeps tests on pushes/PRs. - Tests: year-standing parser tests + export tests (CSV quoting, JSON schema, XLSX structure); CI validates XLSX via openpyxl.
103 lines
4.3 KiB
JavaScript
103 lines
4.3 KiB
JavaScript
const { test } = require('node:test');
|
|
const assert = require('node:assert');
|
|
const {
|
|
buildStudyLoadCsv, buildNextUpCsv, parseScheduleBlocks, csvField
|
|
} = require('../export-csv.js');
|
|
const { buildStudyLoadJson } = require('../export-json.js');
|
|
const { buildStudyLoadXlsx } = require('../export-xlsx.js');
|
|
|
|
const data = {
|
|
year: '2024',
|
|
period: '1ST Semester',
|
|
totalUnits: '21.00',
|
|
header: {
|
|
program: 'BACHELOR OF SCIENCE IN INFORMATION TECHNOLOGY',
|
|
fullName: 'ADRIAN ALFRED CHIONG BONPIN',
|
|
studentId: '22103625',
|
|
semester: '1ST Semester 2024',
|
|
yearLevel: '3'
|
|
},
|
|
courses: [
|
|
{ group: '1', courseCode: 'IT 3101N', description: 'PLATFORM TECHNOLOGIES',
|
|
schedule: 'F 01:30 PM - 04:30 PM LB486', faculty: 'TAN', units: '3.0', status: 'REGULAR' },
|
|
{ group: '1', courseCode: 'CIS 1101', description: 'PROGRAMMING I',
|
|
schedule: 'T Th 03:00 PM - 04:30 PM LB468', faculty: '', units: '3.0', status: 'REGULAR' },
|
|
{ group: '1', courseCode: 'CS 5105', description: 'COMP, TALKS "X"',
|
|
schedule: '', faculty: '', units: '3.00', status: 'DISSOLVED' }
|
|
]
|
|
};
|
|
|
|
// ---- CSV ----
|
|
|
|
test('buildStudyLoadCsv includes header block, table, and total', () => {
|
|
const csv = buildStudyLoadCsv(data);
|
|
assert.ok(csv.startsWith('\uFEFF'), 'has UTF-8 BOM');
|
|
assert.ok(csv.includes('Program,BACHELOR OF SCIENCE IN INFORMATION TECHNOLOGY'));
|
|
assert.ok(csv.includes('Student ID,22103625'));
|
|
assert.ok(csv.includes('Group,Course Code,Description,Schedule,Faculty,Units,Status'));
|
|
assert.ok(csv.includes('1,IT 3101N,PLATFORM TECHNOLOGIES,F 01:30 PM - 04:30 PM LB486,TAN,3.0,REGULAR'));
|
|
assert.ok(/Total,.*21\.00/.test(csv));
|
|
});
|
|
|
|
test('csvField quotes fields containing commas, quotes, newlines', () => {
|
|
assert.strictEqual(csvField('plain'), 'plain');
|
|
assert.strictEqual(csvField('a,b'), '"a,b"');
|
|
assert.strictEqual(csvField('say "hi"'), '"say ""hi"""');
|
|
});
|
|
|
|
// ---- NextUp ----
|
|
|
|
test('buildNextUpCsv matches the Next Up column contract', () => {
|
|
const csv = buildNextUpCsv(data).replace(/^\uFEFF/, '');
|
|
const header = csv.split('\r\n')[0];
|
|
assert.strictEqual(header,
|
|
'title,courseCode,days,startTime,endTime,room,meetingLink,color,icon,units,classLink,professorName,professorEmail,groupName');
|
|
// Schedule blocks -> one row per block, day codes and 24h times.
|
|
assert.ok(csv.includes('PLATFORM TECHNOLOGIES,IT 3101N,FR,13:30,16:30,LB486'));
|
|
assert.ok(csv.includes('PROGRAMMING I,CIS 1101,"TU,TH",15:00,16:30,LB468'));
|
|
});
|
|
|
|
test('parseScheduleBlocks splits multi-block schedules', () => {
|
|
const blocks = parseScheduleBlocks('M 08:00 AM - 09:00 AM R1; W 10:00 AM - 12:00 PM R2');
|
|
assert.ok(Array.isArray(blocks) && blocks.length >= 1);
|
|
assert.strictEqual(Math.max(blocks.length, 0) > 0, true);
|
|
});
|
|
|
|
// ---- JSON ----
|
|
|
|
test('buildStudyLoadJson has improved, clean schema', () => {
|
|
const j = buildStudyLoadJson(data);
|
|
assert.strictEqual(j.meta.program, data.header.program);
|
|
assert.strictEqual(j.meta.studentName, data.header.fullName);
|
|
assert.strictEqual(j.meta.academicYear, '2024');
|
|
assert.strictEqual(j.meta.yearLevel, '3');
|
|
assert.strictEqual(j.meta.totalUnits, '21.00');
|
|
assert.ok(j.meta.generatedAt && /ISO|Z|T/.test(j.meta.generatedAt));
|
|
assert.strictEqual(j.courses.length, 3);
|
|
assert.strictEqual(j.courses[0].courseCode, 'IT 3101N');
|
|
assert.deepStrictEqual(j.totals, { enrolledUnits: '21.00' });
|
|
});
|
|
|
|
// ---- XLSX ----
|
|
|
|
test('buildStudyLoadXlsx returns a valid ZIP with expected parts', () => {
|
|
const bytes = buildStudyLoadXlsx(data);
|
|
assert.ok(bytes instanceof Uint8Array);
|
|
// A valid ZIP starts with PK (0x50 0x4B).
|
|
assert.strictEqual(bytes[0], 0x50);
|
|
assert.strictEqual(bytes[1], 0x4b);
|
|
// Contains XML as text (spot check for the sheet + shared strings).
|
|
const asText = Buffer.from(bytes).toString('latin1');
|
|
assert.ok(asText.includes('Content_Types'), 'content types part present');
|
|
assert.ok(asText.includes('sharedStrings'), 'shared strings part present');
|
|
assert.ok(asText.includes('ISMIS HELPER - STUDY LOAD'), 'title present');
|
|
});
|
|
|
|
test('buildStudyLoadXlsx embeds course data as shared strings', () => {
|
|
const bytes = buildStudyLoadXlsx(data);
|
|
const asText = Buffer.from(bytes).toString('latin1');
|
|
assert.ok(asText.includes('PLATFORM TECHNOLOGIES'));
|
|
assert.ok(asText.includes('IT 3101N'));
|
|
assert.ok(asText.includes('21.00'));
|
|
});
|