Initial commit
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user