'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-jio-withdraw-history-page]' && this.dataset.jioWithdrawHistoryPage ? 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-jio-withdraw-history-list]' ? list : pagination; modal.querySelectorAll = () => closeButtons; const openButton = new Element('button'); const document = { body: { classList: { add() {}, remove() {} } }, querySelector: (selector) => selector === '[data-jio-withdraw-history-modal]' ? modal : null, querySelectorAll: (selector) => selector === '[data-jio-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: { jioData: { restUrl: 'https://example.test/wp-json/jio/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/jio/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.jioWithdrawHistoryPage === '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/); });