| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta name="apple-mobile-web-app-capable" content="yes" />
- <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
- <meta charset="UTF-8">
- <meta name="theme-color" content="#0078d4">
- <script src="../script/jquery.min.js"></script>
- <script src="../script/ao_module.js"></script>
- <title>ao_backend Test</title>
- <style>
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
- body {
- font-family: 'Segoe UI Variable', 'Segoe UI', system-ui, -apple-system, sans-serif;
- font-size: 14px;
- background: #f3f3f3;
- color: #201f1e;
- min-height: 100vh;
- }
- /* ── Nav ───────────────────────────────────────────────── */
- .nav-bar { position: sticky; top: 0; z-index: 100; height: 48px; background: rgba(243,243,243,0.9); backdrop-filter: blur(20px) saturate(180%); border-bottom: 1px solid rgba(0,0,0,0.08); display: flex; align-items: center; padding: 0 20px; gap: 4px; }
- .nav-brand { font-size: 13px; font-weight: 600; color: #201f1e; margin-right: 12px; white-space: nowrap; }
- .nav-tab { display: inline-flex; align-items: center; height: 48px; padding: 0 12px; font-size: 13px; color: #605e5c; text-decoration: none; border-bottom: 2px solid transparent; transition: color 0.1s, border-color 0.1s; white-space: nowrap; }
- .nav-tab:hover { color: #201f1e; }
- .nav-tab.active { color: #0078d4; border-bottom-color: #0078d4; font-weight: 600; }
- /* ── Layout ────────────────────────────────────────────── */
- .content { max-width: 880px; margin: 24px auto; padding: 0 20px; }
- /* ── Toolbar ───────────────────────────────────────────── */
- .toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
- .toolbar-sep { width: 1px; height: 20px; background: #d1d1d1; margin: 0 4px; }
- .summary-text { font-size: 12px; color: #797775; }
- .btn { display: inline-flex; align-items: center; gap: 6px; padding: 6px 16px; border-radius: 4px; font-family: inherit; font-size: 13px; cursor: pointer; border: 1px solid transparent; transition: background 0.1s; line-height: 1; white-space: nowrap; }
- .btn-primary { background: #0078d4; color: #fff; border-color: #0078d4; }
- .btn-primary:hover { background: #106ebe; }
- .btn-primary:disabled { opacity: 0.55; cursor: default; }
- .btn-secondary { background: #fff; color: #201f1e; border-color: #d1d1d1; }
- .btn-secondary:hover { background: #f5f5f5; }
- /* ── Stats pills ───────────────────────────────────────── */
- .stats-bar { display: none; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
- .stat-pill { display: inline-flex; align-items: center; gap: 5px; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; background: #fff; border: 1px solid #e0e0e0; }
- .stat-pill.all { color: #201f1e; }
- .stat-pill.passed { background: #dff6dd; border-color: #bad7b9; color: #107c10; }
- .stat-pill.failed { background: #fde7e9; border-color: #f1b8bb; color: #c50f1f; }
- .stat-pill.pending{ color: #797775; }
- /* ── Test list ─────────────────────────────────────────── */
- .test-list { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.05); margin-bottom: 20px; }
- .section-header {
- padding: 8px 16px 8px 16px;
- font-size: 11px;
- font-weight: 700;
- color: #fff;
- background: #0078d4;
- letter-spacing: 0.04em;
- text-transform: uppercase;
- }
- .section-header.appdata { background: #8764b8; }
- .section-header.file { background: #107c10; }
- .section-header.http { background: #ca5010; }
- .test-list-header { padding: 8px 16px; font-size: 11px; font-weight: 600; color: #797775; text-transform: uppercase; letter-spacing: 0.04em; border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; gap: 12px; }
- .col-name { flex: 1; }
- .col-status { width: 82px; text-align: center; flex-shrink: 0; }
- .col-action { width: 52px; flex-shrink: 0; text-align: right; }
- .test-item { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid #f5f5f5; transition: background 0.1s; }
- .test-item:last-child { border-bottom: none; }
- .test-item:hover { background: #fafafa; }
- .test-idx { font-size: 11px; color: #bbb; width: 22px; text-align: right; flex-shrink: 0; font-variant-numeric: tabular-nums; }
- .test-info { flex: 1; min-width: 0; }
- .test-name { font-size: 13px; font-weight: 500; }
- .test-desc { font-size: 11px; color: #a0a0a0; margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: 'Cascadia Code', 'Consolas', monospace; }
- .status-badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 600; width: 82px; justify-content: center; flex-shrink: 0; }
- .status-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
- .s-pending { background: #f3f3f3; color: #797775; }
- .s-pending .status-dot { background: #c0c0c0; }
- .s-running { background: #eff6fc; color: #0078d4; }
- .s-running .status-dot { background: #0078d4; animation: pulse 1s ease-in-out infinite; }
- .s-pass { background: #dff6dd; color: #107c10; }
- .s-pass .status-dot { background: #107c10; }
- .s-fail { background: #fde7e9; color: #c50f1f; }
- .s-fail .status-dot { background: #c50f1f; }
- @keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.4;transform:scale(.7)} }
- .btn-run { padding: 4px 10px; font-size: 12px; border-radius: 3px; background: transparent; border: 1px solid #d1d1d1; color: #444; cursor: pointer; font-family: inherit; transition: background 0.1s; }
- .btn-run:hover { background: #f0f0f0; }
- .btn-run:disabled { opacity: 0.45; cursor: default; }
- /* ── Result area ───────────────────────────────────────── */
- .result-area { display: none; padding: 10px 16px 12px 50px; font-family: 'Cascadia Code', 'Consolas', monospace; font-size: 12px; line-height: 1.5; background: #fafafa; border-top: 1px solid #f0f0f0; border-left: 3px solid transparent; word-break: break-all; color: #323130; }
- .result-area.r-pass { border-left-color: #107c10; }
- .result-area.r-fail { border-left-color: #c50f1f; }
- </style>
- </head>
- <body>
- <nav class="nav-bar">
- <span class="nav-brand">Unit Tester</span>
- <a class="nav-tab" href="index.html">AGI Scripts</a>
- <a class="nav-tab active" href="backend.html">ao_backend</a>
- <a class="nav-tab" href="wstest.html">WebSocket</a>
- <a class="nav-tab" href="devmode.html">Dev Mode</a>
- </nav>
- <div class="content">
- <div class="toolbar">
- <button class="btn btn-primary" id="btnRunAll" onclick="runAll()">▶ Run All</button>
- <button class="btn btn-secondary" onclick="clearAll()">Clear</button>
- <div class="toolbar-sep"></div>
- <span class="summary-text" id="summaryText">12 tests</span>
- </div>
- <div class="stats-bar" id="statsBar">
- <span class="stat-pill all"><span id="statTotal">12</span> total</span>
- <span class="stat-pill passed">✓ <span id="statPass">0</span> passed</span>
- <span class="stat-pill failed">✗ <span id="statFail">0</span> failed</span>
- <span class="stat-pill pending">· <span id="statPending">12</span> pending</span>
- </div>
- <div id="testContainer"></div>
- </div>
- <script>
- /* ── Test definitions ──────────────────────────────────── */
- const GROUPS = [
- {
- label: "App Data",
- cls: "appdata",
- tests: [
- { name: "appdata.readFile",
- desc: 'appdata.readFile("UnitTest/appdata.txt")',
- run: function(w, ok, fail) {
- w.appdata.readFile("UnitTest/appdata.txt", function(c) { ok(c); });
- }
- },
- { name: "appdata.listDir",
- desc: 'appdata.listDir("UnitTest/")',
- run: function(w, ok, fail) {
- w.appdata.listDir("UnitTest/", function(list) { ok(JSON.stringify(list)); });
- }
- }
- ]
- },
- {
- label: "File System",
- cls: "file",
- tests: [
- { name: "file.writeFile",
- desc: 'file.writeFile("user:/Desktop/hello.txt", "Hello World!")',
- run: function(w, ok, fail) {
- w.file.writeFile("user:/Desktop/hello.txt", "Hello World!", function(r) { ok(r); });
- }
- },
- { name: "file.readFile",
- desc: 'file.readFile("user:/Desktop/hello.txt")',
- run: function(w, ok, fail) {
- w.file.readFile("user:/Desktop/hello.txt", function(c) { ok(c); });
- }
- },
- { name: "file.readdir",
- desc: 'file.readdir("user:/Desktop/")',
- run: function(w, ok, fail) {
- w.file.readdir("user:/Desktop/", function(files) { ok(JSON.stringify(files)); });
- }
- },
- { name: "file.mtime",
- desc: 'file.mtime("user:/Desktop/hello.txt")',
- run: function(w, ok, fail) {
- w.file.mtime("user:/Desktop/hello.txt", function(t) { ok(t); });
- }
- },
- { name: "file.isDir",
- desc: 'file.isDir("user:/Desktop/hello.txt")',
- run: function(w, ok, fail) {
- w.file.isDir("user:/Desktop/hello.txt", function(r) { ok(String(r)); });
- }
- },
- { name: "file.filesize",
- desc: 'file.filesize("user:/Desktop/hello.txt")',
- run: function(w, ok, fail) {
- w.file.filesize("user:/Desktop/hello.txt", function(s) { ok(s + " bytes"); });
- }
- },
- { name: "file.aglob",
- desc: 'file.aglob("user:/Desktop/*.mp3")',
- run: function(w, ok, fail) {
- w.file.aglob("user:/Desktop/*.mp3", undefined, function(r) { ok(JSON.stringify(r)); });
- }
- }
- ]
- },
- {
- label: "HTTP",
- cls: "http",
- tests: [
- { name: "http.get",
- desc: 'http.get("https://www.google.com/robots.txt")',
- run: function(w, ok, fail) {
- w.http.get("https://www.google.com/robots.txt", function(r) {
- ok(r ? r.slice(0, 120) + (r.length > 120 ? "…" : "") : "(empty)");
- });
- }
- },
- { name: "http.post",
- desc: 'http.post("https://www.google.com/robots.txt", undefined)',
- run: function(w, ok, fail) {
- w.http.post("https://www.google.com/robots.txt", undefined, function(r) {
- ok(r ? r.slice(0, 120) + (r.length > 120 ? "…" : "") : "(empty)");
- });
- }
- }
- ]
- }
- ];
- /* ── Flatten for stats ─────────────────────────────────── */
- var allTests = [];
- GROUPS.forEach(function(g) {
- g.tests.forEach(function(t) {
- t.state = "pending";
- t.id = allTests.length;
- allTests.push(t);
- });
- });
- /* ── Backend wrapper (init once) ───────────────────────── */
- var backendWrapper = ao_module_backend();
- backendWrapper.start("UnitTest/ao_backend.js");
- /* ── Build UI ──────────────────────────────────────────── */
- (function buildUI() {
- let container = document.getElementById("testContainer");
- GROUPS.forEach(function(g) {
- let html = '<div class="test-list">' +
- '<div class="section-header ' + g.cls + '">' + g.label + '</div>' +
- '<div class="test-list-header">' +
- '<span style="width:22px;flex-shrink:0"></span>' +
- '<span class="col-name">Test</span>' +
- '<span class="col-status">Status</span>' +
- '<span class="col-action"></span>' +
- '</div>';
- g.tests.forEach(function(t) {
- html += '<div class="test-item" id="item_' + t.id + '">' +
- '<span class="test-idx">' + (t.id + 1) + '</span>' +
- '<div class="test-info">' +
- '<div class="test-name">' + escHtml(t.name) + '</div>' +
- '<div class="test-desc">' + escHtml(t.desc) + '</div>' +
- '</div>' +
- '<span class="status-badge s-pending" id="badge_' + t.id + '">' +
- '<span class="status-dot"></span>Pending' +
- '</span>' +
- '<div class="col-action">' +
- '<button class="btn-run" id="runbtn_' + t.id + '" onclick="runTest(' + t.id + ')">Run</button>' +
- '</div>' +
- '</div>' +
- '<div class="result-area" id="result_' + t.id + '"></div>';
- });
- html += '</div>';
- container.innerHTML += html;
- });
- updateStats();
- document.getElementById("statsBar").style.display = "flex";
- })();
- /* ── Run logic ─────────────────────────────────────────── */
- function runTest(id) {
- let t = allTests[id];
- let btn = document.getElementById("runbtn_" + id);
- setStatus(id, "running");
- if (btn) btn.disabled = true;
- let timer = setTimeout(function() {
- setStatus(id, "fail");
- showResult(id, "r-fail", "Timeout — no response after 10 s");
- if (btn) btn.disabled = false;
- }, 10000);
- t.run(backendWrapper,
- function(result) { // ok
- clearTimeout(timer);
- setStatus(id, "pass");
- showResult(id, "r-pass", escHtml(String(result || "(empty)")));
- if (btn) btn.disabled = false;
- },
- function(reason) { // fail
- clearTimeout(timer);
- setStatus(id, "fail");
- showResult(id, "r-fail", escHtml(String(reason || "Unknown error")));
- if (btn) btn.disabled = false;
- }
- );
- }
- function runAll() {
- let btn = document.getElementById("btnRunAll");
- btn.disabled = true;
- btn.textContent = "Running…";
- allTests.forEach(function(t) { runTest(t.id); });
- let check = setInterval(function() {
- if (!allTests.some(function(t) { return t.state === "running"; })) {
- clearInterval(check);
- btn.disabled = false;
- btn.innerHTML = "▶ Run All";
- }
- }, 300);
- }
- function clearAll() {
- allTests.forEach(function(t) {
- t.state = "pending";
- setStatus(t.id, "pending");
- let r = document.getElementById("result_" + t.id);
- if (r) { r.style.display = "none"; r.innerHTML = ""; r.className = "result-area"; }
- });
- updateStats();
- }
- /* ── Helpers ───────────────────────────────────────────── */
- function setStatus(id, state) {
- allTests[id].state = state;
- let badge = document.getElementById("badge_" + id);
- if (!badge) return;
- let labels = { pending:"Pending", running:"Running", pass:"Passed", fail:"Failed" };
- badge.className = "status-badge s-" + state;
- badge.innerHTML = '<span class="status-dot"></span>' + labels[state];
- updateStats();
- }
- function showResult(id, cls, html) {
- let div = document.getElementById("result_" + id);
- if (!div) return;
- div.className = "result-area " + cls;
- div.innerHTML = html;
- div.style.display = "block";
- }
- function updateStats() {
- let total = allTests.length;
- let passed = allTests.filter(function(t) { return t.state === "pass"; }).length;
- let failed = allTests.filter(function(t) { return t.state === "fail"; }).length;
- let pending = allTests.filter(function(t) { return t.state !== "pass" && t.state !== "fail"; }).length;
- document.getElementById("statTotal").textContent = total;
- document.getElementById("statPass").textContent = passed;
- document.getElementById("statFail").textContent = failed;
- document.getElementById("statPending").textContent = pending;
- }
- function escHtml(s) {
- return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");
- }
- </script>
- </body>
- </html>
|