@sun-asterisk/sungen 2.6.10 → 2.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1 -1
- package/dist/dashboard/templates/index.html +112 -101
- package/dist/exporters/xlsx-exporter.d.ts +6 -0
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +53 -19
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +4 -3
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/dashboard/templates/index.html +112 -101
- package/src/exporters/xlsx-exporter.ts +58 -19
- package/src/orchestrator/project-initializer.ts +4 -3
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import * as fs from 'fs';
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import ExcelJS from 'exceljs';
|
|
15
|
+
import JSZip from 'jszip';
|
|
15
16
|
import { ScreenSummary, TestCaseRow } from './types';
|
|
16
17
|
import { getPackageVersion } from './package-info';
|
|
17
18
|
import { SUN_LOGO_PNG_BASE64 } from './sun-logo';
|
|
@@ -54,7 +55,7 @@ export function renderXlsx(
|
|
|
54
55
|
|
|
55
56
|
// -- Column widths matching template_report.xlsx (Sample sheet) --
|
|
56
57
|
ws.columns = [
|
|
57
|
-
{ width:
|
|
58
|
+
{ width: 20 }, // A — TC ID (wider to fit long flow IDs like FLOW-KUDO-…)
|
|
58
59
|
{ width: 12.5 }, // B — Screen/Function
|
|
59
60
|
{ width: 15.38 }, // C — Big item
|
|
60
61
|
{ width: 16.25 }, // D — Medium item
|
|
@@ -92,28 +93,24 @@ export function renderXlsx(
|
|
|
92
93
|
ws.mergeCells('A4:F4');
|
|
93
94
|
ws.mergeCells('G4:H4');
|
|
94
95
|
|
|
95
|
-
// A1 (logo band, merged A1:C3) —
|
|
96
|
+
// A1 (logo band, merged A1:C3) — border + embedded Sun* logo (base64 inline).
|
|
96
97
|
const a1 = ws.getCell('A1');
|
|
97
98
|
a1.alignment = { horizontal: 'center', vertical: 'middle' };
|
|
98
99
|
a1.border = allBordersBlack;
|
|
99
100
|
|
|
100
|
-
// Embed Sun* logo using a oneCellAnchor + explicit pixel `ext`, exactly
|
|
101
|
-
// like the template. tl is offset into the merged band so the logo lands
|
|
102
|
-
// centred in A1:C3 instead of pinned to the top-left corner.
|
|
103
101
|
try {
|
|
104
102
|
const imageId = wb.addImage({
|
|
105
103
|
buffer: Buffer.from(SUN_LOGO_PNG_BASE64, 'base64') as unknown as ExcelJS.Buffer,
|
|
106
104
|
extension: 'png',
|
|
107
105
|
});
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
// the same field names).
|
|
106
|
+
// Centre a fixed-size 90×51 logo inside A1:C3 using absolute EMU offsets.
|
|
107
|
+
// Col widths (A=20, B=12.5, C=15.38) → pixels via Excel's `w*7+5` rule:
|
|
108
|
+
// A=145, B=92.5, C=112.66 ⇒ A1:C3 ≈ 350 px wide.
|
|
109
|
+
// Left padding for a 90-px image = (350−90)/2 ≈ 130 px = 1,237,500 EMU.
|
|
110
|
+
// 3 default rows × 20 px = 60 px; top padding ≈ 4.5 px ≈ 42,862 EMU.
|
|
114
111
|
ws.addImage(imageId, {
|
|
115
|
-
tl: { nativeCol: 0, nativeColOff:
|
|
116
|
-
ext: { width: 90, height:
|
|
112
|
+
tl: { nativeCol: 0, nativeColOff: 1237500, nativeRow: 0, nativeRowOff: 42862 } as unknown as ExcelJS.Anchor,
|
|
113
|
+
ext: { width: 90, height: 51 },
|
|
117
114
|
editAs: 'oneCell',
|
|
118
115
|
} as unknown as ExcelJS.ImageRange);
|
|
119
116
|
} catch { /* logo is decorative — never block export */ }
|
|
@@ -161,8 +158,7 @@ export function renderXlsx(
|
|
|
161
158
|
g4.alignment = { horizontal: 'right', vertical: 'middle', wrapText: true };
|
|
162
159
|
g4.border = allBordersBlack;
|
|
163
160
|
|
|
164
|
-
// -- Row 5: spacer (height
|
|
165
|
-
ws.getRow(5).height = 12;
|
|
161
|
+
// -- Row 5: spacer (height auto-fit). --
|
|
166
162
|
|
|
167
163
|
// -- Row 6: Summary headers (cols C..H), lavender fill --
|
|
168
164
|
const sumLabels: Record<string, string> = {
|
|
@@ -176,7 +172,7 @@ export function renderXlsx(
|
|
|
176
172
|
c.alignment = { horizontal: 'center', vertical: 'top' };
|
|
177
173
|
c.border = allBordersBlack;
|
|
178
174
|
}
|
|
179
|
-
|
|
175
|
+
// No explicit height → Excel auto-fits this row's content.
|
|
180
176
|
|
|
181
177
|
// -- Row 7: Counts (cols C..H), live formulas referencing the data area --
|
|
182
178
|
// Data table starts at row 15 (col header at row 14, divider at row 14? actually
|
|
@@ -200,7 +196,7 @@ export function renderXlsx(
|
|
|
200
196
|
c.border = allBordersBlack;
|
|
201
197
|
if (col === 'H') c.numFmt = '#,##0';
|
|
202
198
|
}
|
|
203
|
-
|
|
199
|
+
// No explicit height → Excel auto-fits.
|
|
204
200
|
|
|
205
201
|
// C8 has no value in the template but still needs a border so the band
|
|
206
202
|
// visually closes underneath "Total TCs".
|
|
@@ -227,7 +223,7 @@ export function renderXlsx(
|
|
|
227
223
|
c.border = allBordersBlack;
|
|
228
224
|
c.numFmt = '0%';
|
|
229
225
|
}
|
|
230
|
-
|
|
226
|
+
// No explicit height → Excel auto-fits.
|
|
231
227
|
|
|
232
228
|
// After rows 1-8 are populated, append a blank row 9 spacer.
|
|
233
229
|
while (ws.rowCount < 9) ws.addRow([]);
|
|
@@ -353,6 +349,17 @@ export function renderXlsx(
|
|
|
353
349
|
to: { row: ws.rowCount, column: COL_COUNT },
|
|
354
350
|
};
|
|
355
351
|
|
|
352
|
+
// Sheet protection lets the picLocks on the embedded logo take effect
|
|
353
|
+
// (Excel honours image locks only when the sheet is protected). Empty
|
|
354
|
+
// password — QA can run Review → Unprotect Sheet if they need to edit.
|
|
355
|
+
// `objects: false` here is ExcelJS's inverted semantic that writes
|
|
356
|
+
// `objects="1"` in XML (= objects ARE locked).
|
|
357
|
+
(ws as unknown as { sheetProtection: object }).sheetProtection = {
|
|
358
|
+
sheet: true,
|
|
359
|
+
objects: false,
|
|
360
|
+
scenarios: false,
|
|
361
|
+
};
|
|
362
|
+
|
|
356
363
|
return wb;
|
|
357
364
|
}
|
|
358
365
|
|
|
@@ -368,9 +375,41 @@ export async function writeXlsx(
|
|
|
368
375
|
const outDir = path.join(cwd, 'qa', 'deliverables');
|
|
369
376
|
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
370
377
|
const outPath = path.join(outDir, `${screen}-testcases.xlsx`);
|
|
371
|
-
await wb.xlsx.
|
|
378
|
+
const buffer = await wb.xlsx.writeBuffer();
|
|
379
|
+
const locked = await lockEmbeddedImages(Buffer.from(buffer as ArrayBuffer));
|
|
380
|
+
fs.writeFileSync(outPath, locked);
|
|
372
381
|
return outPath;
|
|
373
382
|
}
|
|
374
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Post-process the workbook buffer: extend `<a:picLocks>` on every drawing
|
|
386
|
+
* with `noMove`, `noResize`, `noSelect`. Combined with the sheet protection
|
|
387
|
+
* above, the embedded logo can't be dragged, resized, or selected via the UI.
|
|
388
|
+
*/
|
|
389
|
+
export async function lockEmbeddedImages(buffer: Buffer): Promise<Buffer> {
|
|
390
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
391
|
+
const drawingFiles = Object.keys(zip.files).filter(
|
|
392
|
+
(name) => /^xl\/drawings\/drawing\d+\.xml$/.test(name)
|
|
393
|
+
);
|
|
394
|
+
for (const name of drawingFiles) {
|
|
395
|
+
const file = zip.file(name);
|
|
396
|
+
if (!file) continue;
|
|
397
|
+
const xml = await file.async('string');
|
|
398
|
+
const patched = xml.replace(
|
|
399
|
+
/<a:picLocks([^/]*)\/>/g,
|
|
400
|
+
(_match, attrs: string) => {
|
|
401
|
+
const has = (k: string) => new RegExp(`\\b${k}=`).test(attrs);
|
|
402
|
+
const additions = ['noMove', 'noResize', 'noSelect']
|
|
403
|
+
.filter((k) => !has(k))
|
|
404
|
+
.map((k) => ` ${k}="1"`)
|
|
405
|
+
.join('');
|
|
406
|
+
return `<a:picLocks${attrs}${additions}/>`;
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
zip.file(name, patched);
|
|
410
|
+
}
|
|
411
|
+
return zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' });
|
|
412
|
+
}
|
|
413
|
+
|
|
375
414
|
void applyBorder;
|
|
376
415
|
void ({} as AnyRow);
|
|
@@ -450,8 +450,9 @@ export class ProjectInitializer {
|
|
|
450
450
|
console.log(`📦 Installing ${missingDeps.join(', ')}...\n`);
|
|
451
451
|
execSync(`npm install -D ${missingDeps.join(' ')}`, execOpts);
|
|
452
452
|
|
|
453
|
-
console.log('\n🎭 Installing
|
|
454
|
-
execSync('npx playwright install', execOpts);
|
|
453
|
+
console.log('\n🎭 Installing Chromium (default browser)...\n');
|
|
454
|
+
execSync('npx playwright install --with-deps chromium', execOpts);
|
|
455
|
+
console.log('\n💡 To install other browsers: npm run install:browsers -- firefox webkit\n');
|
|
455
456
|
}
|
|
456
457
|
|
|
457
458
|
/**
|
|
@@ -477,7 +478,7 @@ export class ProjectInitializer {
|
|
|
477
478
|
'test:ui': 'playwright test specs/generated/ --ui',
|
|
478
479
|
'report': 'playwright show-report',
|
|
479
480
|
'generate': 'sungen generate --all',
|
|
480
|
-
'install:browsers': 'npx playwright install
|
|
481
|
+
'install:browsers': 'npx playwright install',
|
|
481
482
|
};
|
|
482
483
|
|
|
483
484
|
let added = 0;
|