230 lines
8.8 KiB
JavaScript
230 lines
8.8 KiB
JavaScript
import { test, describe, before } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'fs';
|
|
import { JSDOM } from 'jsdom';
|
|
import crypto from 'node:crypto';
|
|
|
|
describe('Secret Message Studio - Comprehensive Test Suite', () => {
|
|
let dom;
|
|
let window;
|
|
let document;
|
|
|
|
before(async () => {
|
|
const htmlContent = fs.readFileSync('index.html', 'utf8');
|
|
|
|
dom = new JSDOM(htmlContent, {
|
|
url: 'http://localhost/index.html',
|
|
runScripts: 'dangerously',
|
|
beforeParse(win) {
|
|
// Polyfill Web Crypto API
|
|
Object.defineProperty(win, 'crypto', {
|
|
value: crypto.webcrypto,
|
|
writable: true,
|
|
});
|
|
|
|
// Mock QRCode canvas drawer to prevent external network fetches
|
|
win.QRCode = {
|
|
toCanvas: (canvas, text, opts) => {
|
|
const ctx = canvas.getContext('2d');
|
|
if (ctx) {
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
}
|
|
},
|
|
};
|
|
|
|
// Polyfill animationFrame
|
|
win.requestAnimationFrame = (cb) => setTimeout(cb, 16);
|
|
win.cancelAnimationFrame = (id) => clearTimeout(id);
|
|
},
|
|
});
|
|
|
|
window = dom.window;
|
|
document = window.document;
|
|
|
|
// Allow inline script execution to settle
|
|
await new Promise((r) => setTimeout(r, 100));
|
|
});
|
|
|
|
test('DOM elements initialized properly', () => {
|
|
assert.ok(document.querySelector('#messageInput'), 'messageInput exists');
|
|
assert.ok(document.querySelector('#createBtn'), 'createBtn exists');
|
|
assert.ok(document.querySelector('#openBtn'), 'openBtn exists');
|
|
assert.ok(document.querySelector('#decodedResult'), 'decodedResult exists');
|
|
});
|
|
|
|
test('Language toggle works (DE -> EN -> DE)', () => {
|
|
const deBtn = document.querySelector('[data-lang="de"]');
|
|
const enBtn = document.querySelector('[data-lang="en"]');
|
|
|
|
enBtn.click();
|
|
assert.strictEqual(window.state.lang, 'en');
|
|
assert.strictEqual(document.querySelector('#createBtn').textContent, 'Create');
|
|
|
|
deBtn.click();
|
|
assert.strictEqual(window.state.lang, 'de');
|
|
assert.strictEqual(document.querySelector('#createBtn').textContent, 'Erstellen');
|
|
});
|
|
|
|
test('Tab navigation works (Encode <-> Decode)', () => {
|
|
const decodeTab = document.querySelector('[data-tab="decode"]');
|
|
const encodeTab = document.querySelector('[data-tab="encode"]');
|
|
|
|
decodeTab.click();
|
|
assert.strictEqual(window.state.tab, 'decode');
|
|
assert.ok(document.querySelector('#decodeView').classList.contains('active'));
|
|
|
|
encodeTab.click();
|
|
assert.strictEqual(window.state.tab, 'encode');
|
|
assert.ok(document.querySelector('#encodeView').classList.contains('active'));
|
|
});
|
|
|
|
test('Password Generator button works', () => {
|
|
const genBtn = document.querySelector('#generatePasswordBtn');
|
|
const passInput = document.querySelector('#passwordInput');
|
|
const secureToggle = document.querySelector('#secureToggle');
|
|
|
|
genBtn.click();
|
|
assert.ok(passInput.value.length >= 12, 'Password generated with sufficient length');
|
|
assert.strictEqual(secureToggle.checked, true, 'Secure toggle activated');
|
|
});
|
|
|
|
test('Base64 Encoding & Decoding flow', async () => {
|
|
document.querySelector('#clearEncodeBtn').click();
|
|
document.querySelector('#messageInput').value = 'Test Secret Message 123';
|
|
document.querySelector('#toInput').value = 'Alice';
|
|
document.querySelector('#fromInput').value = 'Bob';
|
|
|
|
document.querySelector('#createBtn').click();
|
|
|
|
// Give microtask queue time to finish async createPayload
|
|
await new Promise((r) => setTimeout(r, 150));
|
|
|
|
assert.ok(window.state.payload.startsWith('sm2.'), 'Payload generated with sm2 prefix');
|
|
const displayCode = window.state.displayCode;
|
|
assert.ok(displayCode, 'Display code is present');
|
|
|
|
// Switch to decode and open message with fade mode
|
|
document.querySelector('[data-tab="decode"]').click();
|
|
document.querySelector('#revealMode').value = 'fade';
|
|
document.querySelector('#decodeInput').value = displayCode;
|
|
|
|
document.querySelector('#openBtn').click();
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
|
|
const resultText = document.querySelector('#decodedResult').textContent;
|
|
assert.strictEqual(resultText, 'Test Secret Message 123', 'Message correctly decoded');
|
|
});
|
|
|
|
test('AES Password Encryption & Decryption flow', async () => {
|
|
document.querySelector('[data-tab="encode"]').click();
|
|
document.querySelector('#clearEncodeBtn').click();
|
|
|
|
document.querySelector('#messageInput').value = 'Top Secret Encrypted';
|
|
document.querySelector('#passwordInput').value = 'MySuperSecretPass123!';
|
|
document.querySelector('#secureToggle').checked = true;
|
|
|
|
document.querySelector('#createBtn').click();
|
|
await new Promise((r) => setTimeout(r, 150));
|
|
|
|
const payload = window.state.payload;
|
|
assert.ok(payload.startsWith('sm2.'), 'Encrypted payload created');
|
|
|
|
// Decode with wrong password -> should fail
|
|
document.querySelector('[data-tab="decode"]').click();
|
|
document.querySelector('#revealMode').value = 'fade';
|
|
document.querySelector('#decodeInput').value = payload;
|
|
document.querySelector('#decodePassword').value = 'WrongPassword';
|
|
|
|
document.querySelector('#openBtn').click();
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
|
|
assert.strictEqual(document.querySelector('#toast').textContent, 'Dieses Geheimnis braucht das richtige Passwort.');
|
|
|
|
// Decode with correct password -> should succeed
|
|
document.querySelector('#decodePassword').value = 'MySuperSecretPass123!';
|
|
document.querySelector('#openBtn').click();
|
|
await new Promise((r) => setTimeout(r, 300));
|
|
|
|
assert.strictEqual(document.querySelector('#decodedResult').textContent, 'Top Secret Encrypted');
|
|
});
|
|
|
|
test('Decoy Message flow on wrong password', async () => {
|
|
document.querySelector('[data-tab="encode"]').click();
|
|
document.querySelector('#clearEncodeBtn').click();
|
|
|
|
document.querySelector('#messageInput').value = 'Real Secret';
|
|
document.querySelector('#passwordInput').value = 'RealPassword123';
|
|
document.querySelector('#secureToggle').checked = true;
|
|
document.querySelector('#decoyInput').value = 'Fake Decoy Message';
|
|
|
|
document.querySelector('#createBtn').click();
|
|
await new Promise((r) => setTimeout(r, 150));
|
|
|
|
const payload = window.state.payload;
|
|
|
|
// Decode with wrong password -> should display Decoy message
|
|
document.querySelector('[data-tab="decode"]').click();
|
|
document.querySelector('#revealMode').value = 'fade';
|
|
document.querySelector('#decodeInput').value = payload;
|
|
document.querySelector('#decodePassword').value = 'WrongPassword';
|
|
|
|
document.querySelector('#openBtn').click();
|
|
await new Promise((r) => setTimeout(r, 300));
|
|
|
|
assert.strictEqual(document.querySelector('#decodedResult').textContent, 'Fake Decoy Message');
|
|
});
|
|
|
|
test('Emoji Code conversion', () => {
|
|
const originalText = 'sm2.SGVsbG8';
|
|
const emojiCode = window.toEmojiCode(originalText);
|
|
const convertedBack = window.fromEmojiCode(emojiCode);
|
|
assert.strictEqual(convertedBack, originalText, 'Emoji code roundtrip succeeds');
|
|
});
|
|
|
|
test('Steganography Embedding & Extraction', () => {
|
|
const canvas = window.document.createElement('canvas');
|
|
canvas.width = 400;
|
|
canvas.height = 400;
|
|
const ctx = canvas.getContext('2d');
|
|
ctx.fillStyle = '#ff0000';
|
|
ctx.fillRect(0, 0, 400, 400);
|
|
|
|
const testPayload = 'sm2.StegoPayloadTesting123';
|
|
const success = window.embedPayloadInCanvas(canvas, testPayload);
|
|
assert.strictEqual(success, true, 'Payload successfully embedded in canvas');
|
|
|
|
const extracted = window.extractPayloadFromCanvas(canvas);
|
|
assert.strictEqual(extracted, testPayload, 'Extracted payload matches original');
|
|
});
|
|
|
|
test('Design Undo & Redo functionality', () => {
|
|
const initialQrX = window.state.design.qrX;
|
|
|
|
// Change slider
|
|
const qrXRange = document.querySelector('#qrXRange');
|
|
qrXRange.value = '75';
|
|
qrXRange.dispatchEvent(new window.Event('input'));
|
|
|
|
assert.strictEqual(window.state.design.qrX, 75);
|
|
|
|
// Click Undo
|
|
document.querySelector('#undoDesignBtn').click();
|
|
assert.strictEqual(window.state.design.qrX, initialQrX, 'Undo restored initial QR X');
|
|
|
|
// Click Redo
|
|
document.querySelector('#redoDesignBtn').click();
|
|
assert.strictEqual(window.state.design.qrX, 75, 'Redo restored updated QR X');
|
|
});
|
|
|
|
test('Clear Encode & Clear Decode buttons', () => {
|
|
document.querySelector('#messageInput').value = 'Some message';
|
|
document.querySelector('#clearEncodeBtn').click();
|
|
assert.strictEqual(document.querySelector('#messageInput').value, '', 'Message input cleared');
|
|
|
|
document.querySelector('#decodeInput').value = 'Some code';
|
|
document.querySelector('#clearDecodeBtn').click();
|
|
assert.strictEqual(document.querySelector('#decodeInput').value, '', 'Decode input cleared');
|
|
});
|
|
});
|