fix(theme): align reference layouts and restore passing contracts
Align homepage and category discovery, modern widgets, admin controls, quotes, and comments with the reference theme. Remove superseded paths and add rendering and behavior coverage. Update stale asset and finance contracts; all 760 Node tests and 139 widget assertions pass.
This commit is contained in:
1 parent
b520d29368
commit
8aa116ec43
63 files changed
+1985
-2304
No files matched your search
@@ -0,0 +1,139 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
const test = require('node:test');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
|
||||
class Element {
|
||||
constructor(tag = 'div') {
|
||||
this.tagName = tag.toUpperCase();
|
||||
this.children = [];
|
||||
this.dataset = {};
|
||||
this.attributes = {};
|
||||
this.disabled = false;
|
||||
this.hidden = false;
|
||||
this.listeners = {};
|
||||
this._textContent = '';
|
||||
this.className = '';
|
||||
this.classList = {
|
||||
values: new Set(),
|
||||
add: (...values) => values.forEach((value) => this.classList.values.add(value)),
|
||||
};
|
||||
}
|
||||
|
||||
set textContent(value) {
|
||||
this._textContent = String(value);
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
get textContent() {
|
||||
return this._textContent + this.children.map((child) => child.textContent || '').join('');
|
||||
}
|
||||
|
||||
appendChild(child) {
|
||||
this.children.push(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
addEventListener(type, callback) {
|
||||
this.listeners[type] = callback;
|
||||
}
|
||||
|
||||
setAttribute(name, value) {
|
||||
this.attributes[name] = String(value);
|
||||
}
|
||||
|
||||
closest(selector) {
|
||||
return selector === '[data-aksrt-withdraw-history-page]' && this.dataset.aksrtWithdrawHistoryPage
|
||||
? this
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
function textContent(node) {
|
||||
return node.textContent;
|
||||
}
|
||||
|
||||
test('withdrawal history requests paged ten-item results and renders the returned page', async () => {
|
||||
const requests = [];
|
||||
const pagePayloads = {
|
||||
1: {
|
||||
items: [{ status: 'completed', gross_amount: '10.00', amount: '9.50', withdrawal_number: 'W1', created_at: '2026-10-01', note: 'private note' }],
|
||||
page: 1,
|
||||
max_pages: 2,
|
||||
},
|
||||
2: {
|
||||
items: [{ status: 'pending', gross_amount: '20.00', amount: '20.00', withdrawal_number: 'W2', created_at: '2026-10-02' }],
|
||||
page: 2,
|
||||
max_pages: 2,
|
||||
},
|
||||
};
|
||||
const list = new Element();
|
||||
const pagination = new Element('nav');
|
||||
const closeButtons = [];
|
||||
const modal = new Element();
|
||||
modal.querySelector = (selector) => selector === '[data-aksrt-withdraw-history-list]' ? list : pagination;
|
||||
modal.querySelectorAll = () => closeButtons;
|
||||
const openButton = new Element('button');
|
||||
const document = {
|
||||
body: { classList: { add() {}, remove() {} } },
|
||||
querySelector: (selector) => selector === '[data-aksrt-withdraw-history-modal]' ? modal : null,
|
||||
querySelectorAll: (selector) => selector === '[data-aksrt-withdraw-history-open]' ? [openButton] : [],
|
||||
createElement: (tag) => new Element(tag),
|
||||
createTextNode: (text) => ({ textContent: String(text) }),
|
||||
};
|
||||
let init;
|
||||
const jQuery = () => ({
|
||||
off() { return this; },
|
||||
on(_event, callback) { init = callback; return this; },
|
||||
});
|
||||
const fetch = (url) => {
|
||||
requests.push(url);
|
||||
const page = Number(new URL(url).searchParams.get('page'));
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(pagePayloads[page]),
|
||||
});
|
||||
};
|
||||
const source = fs.readFileSync(path.join(root, 'assets/js/credits-withdraw-history.js'), 'utf8');
|
||||
|
||||
vm.runInNewContext(source, {
|
||||
window: { aksrtData: { restUrl: 'https://example.test/wp-json/aksrt/v1', restNonce: 'nonce' } },
|
||||
document,
|
||||
jQuery,
|
||||
fetch,
|
||||
Array,
|
||||
Number,
|
||||
String,
|
||||
Math,
|
||||
parseInt,
|
||||
Promise,
|
||||
Error,
|
||||
});
|
||||
|
||||
assert.equal(typeof init, 'function', 'module registers its lifecycle initializer');
|
||||
init();
|
||||
openButton.listeners.click({ preventDefault() {} });
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
assert.equal(requests.length, 1);
|
||||
assert.equal(new URL(requests[0]).pathname, '/wp-json/aksrt/v1/earnings/withdrawals');
|
||||
assert.equal(new URL(requests[0]).searchParams.get('page'), '1');
|
||||
assert.equal(new URL(requests[0]).searchParams.get('per_page'), '10');
|
||||
assert.match(textContent(list.children[0]), /实际到账:9\.50元/);
|
||||
assert.doesNotMatch(textContent(list.children[0]), /private note/);
|
||||
|
||||
const pageTwo = pagination.children.find((button) => button.dataset.aksrtWithdrawHistoryPage === '2');
|
||||
assert.ok(pageTwo, 'the API max_pages value creates a second-page control');
|
||||
pagination.listeners.click({ target: pageTwo, preventDefault() {} });
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(new URL(requests[1]).searchParams.get('page'), '2');
|
||||
assert.equal(new URL(requests[1]).searchParams.get('per_page'), '10');
|
||||
assert.match(textContent(list.children[0]), /W2/);
|
||||
});
|
||||
Reference in new issue
Block a user