Spaces:
Running
Running
Retain local and live browser evidence for request-tree TraceChain deployment
Browse files
evidence/live-browser.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
evidence/local-browser.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
source/browser-sqlite/scripts/verify-space.mjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import assert from 'node:assert/strict';
|
| 2 |
+
import {writeFileSync} from 'node:fs';
|
| 3 |
+
import puppeteer from 'puppeteer-core';
|
| 4 |
+
|
| 5 |
+
const url = process.env.TEAQL_SPACE_URL || 'https://teaql-browser-sqlite-playground.static.hf.space/';
|
| 6 |
+
const browser = await puppeteer.launch({executablePath: process.env.CHROME_BIN || '/usr/bin/google-chrome',
|
| 7 |
+
headless: true, args: ['--no-sandbox', '--disable-dev-shm-usage']});
|
| 8 |
+
try {
|
| 9 |
+
const page = await browser.newPage(), errors = [];
|
| 10 |
+
page.on('pageerror', error => errors.push(error.message));
|
| 11 |
+
page.on('console', event => { if (event.type() === 'error') errors.push(event.text()); });
|
| 12 |
+
page.on('response', response => { if (response.status() >= 400) errors.push(`${response.status()} ${response.url()}`); });
|
| 13 |
+
page.on('requestfailed', request => errors.push(`${request.failure()?.errorText} ${request.url()}`));
|
| 14 |
+
await page.goto(url, {waitUntil: 'networkidle0', timeout: 60_000});
|
| 15 |
+
const waitReady = () => page.waitForFunction(() => document.querySelector('#status')?.textContent?.includes('ready'), {timeout: 45_000});
|
| 16 |
+
await waitReady();
|
| 17 |
+
async function inspect(label) {
|
| 18 |
+
const response = JSON.parse(await page.$eval('#result', element => element.textContent));
|
| 19 |
+
const evidence = JSON.parse(await page.$eval('#sql', element => element.textContent));
|
| 20 |
+
assert.equal(response.resultCode, 0); assert.equal(response.status, 'YES');
|
| 21 |
+
assert.equal(response.recordCount, 3); assert.equal(response.metadata.totalCount, 3);
|
| 22 |
+
assert.equal(response.data[0].orderNumber, 'KEEP-002'); assert.equal(response.data[0].itemCount, 5);
|
| 23 |
+
assert.deepEqual(response.data[0].items.map(item => item.productName), ['Box 1', 'Box 2', 'Box 3']);
|
| 24 |
+
assert.deepEqual(response.facets.statusCounts.map(row => [row.orderCount, row.orderTotalAmount]), [[1, 100], [2, 500], [0, null]]);
|
| 25 |
+
assert.equal(evidence.length, 8);
|
| 26 |
+
const chains = evidence.map(entry => entry.traceChain.filter(node => node.kind === 'comment').map(node => node.detail));
|
| 27 |
+
for (const expected of [
|
| 28 |
+
['load order list', "countAs('totalOrders')"],
|
| 29 |
+
['load order list', 'selectStatusWith(...)', 'load status'],
|
| 30 |
+
['load order list', 'selectOrderItemListWith(...)', 'load item previews'],
|
| 31 |
+
['load order list', "countOrderItemsAs('itemCount')"],
|
| 32 |
+
['load order list', "facetByStatusAs('statusCounts')", 'aggregate status'],
|
| 33 |
+
['load order list', "facetByStatusAs('statusCounts')", 'load status facets'],
|
| 34 |
+
]) assert.ok(chains.some(chain => JSON.stringify(chain) === JSON.stringify(expected)), JSON.stringify(expected));
|
| 35 |
+
const rendered = await page.$$eval('#evidence-rows tr', rows => rows.map(row => [...row.querySelectorAll('td')].map(cell => cell.textContent)));
|
| 36 |
+
assert.deepEqual(rendered, evidence.map(entry => [`${entry.elapsedMicros} µs`,
|
| 37 |
+
entry.tracePath.find(node => node.kind === 'request')?.name ?? '—',
|
| 38 |
+
`${entry.traceChain.find(node => node.kind === 'purpose').detail}: ${entry.traceChain.filter(node => node.kind === 'comment').map(node => node.detail).join(' → ')}`,
|
| 39 |
+
entry.debugSQL]));
|
| 40 |
+
return {label, response, evidence, rendered};
|
| 41 |
+
}
|
| 42 |
+
const receipts = [await inspect('first')];
|
| 43 |
+
const host = await page.evaluate(() => ({crossOriginIsolated, opfsDisabled: document.querySelector('#persist').disabled}));
|
| 44 |
+
if (!host.crossOriginIsolated) assert.equal(host.opfsDisabled, true);
|
| 45 |
+
await page.click('#logging');
|
| 46 |
+
await page.waitForFunction(() => document.querySelector('#status')?.textContent?.includes('0 diagnostics'));
|
| 47 |
+
receipts.push(await inspect('diagnostics-off'));
|
| 48 |
+
await page.select('#example', 'all');
|
| 49 |
+
await page.waitForFunction(() => { const text = document.querySelector('#result')?.textContent; return text && JSON.parse(text).recordCount === 4; });
|
| 50 |
+
await page.$eval('#search', element => { element.value = 'Q.constructor("return globalThis")'; });
|
| 51 |
+
await page.click('#query');
|
| 52 |
+
await page.waitForFunction(() => document.querySelector('#status')?.textContent?.includes('not allowed'));
|
| 53 |
+
assert.equal(await page.$eval('#sql', element => element.textContent), '[]');
|
| 54 |
+
await page.click('#preset'); await waitReady(); receipts.push(await inspect('restored'));
|
| 55 |
+
await page.click('#reset');
|
| 56 |
+
await page.waitForFunction(() => !document.querySelector('#reset').disabled);
|
| 57 |
+
receipts.push(await inspect('reset'));
|
| 58 |
+
await page.reload({waitUntil: 'networkidle0'}); await waitReady(); receipts.push(await inspect('refresh'));
|
| 59 |
+
assert.deepEqual(errors, []);
|
| 60 |
+
if (process.env.TEAQL_SPACE_EVIDENCE) writeFileSync(process.env.TEAQL_SPACE_EVIDENCE,
|
| 61 |
+
JSON.stringify({url, host, receipts, queryPresetSwitch: true, forbiddenConstructorRejected: true, errors, passed: true}, null, 2) + '\n', {flag: 'wx'});
|
| 62 |
+
console.log(`PASS live Static Space: new TraceChain, 8 SQL records, editor/presets, WebResponse/facets, diagnostics-off, reset/refresh; OPFS disabled=${host.opfsDisabled}`);
|
| 63 |
+
} finally { await browser.close(); }
|