fix(signin): align check-in feedback with reference theme
This commit is contained in:
1 parent
1df2822f01
commit
8e9d3a7e49
7 files changed
+334
-65
No files matched your search
+56
-58
@@ -11,6 +11,7 @@
|
||||
}
|
||||
|
||||
var signinBusy = false;
|
||||
var refreshSequence = 0;
|
||||
|
||||
function api(path, options) {
|
||||
return window.AksrtRequest.request(path, options || {}).then(function (response) {
|
||||
@@ -60,24 +61,17 @@
|
||||
});
|
||||
}
|
||||
|
||||
function setButtonState(state) {
|
||||
currentButtons().forEach(function (button) {
|
||||
var label = button.querySelector('[data-signin-label]');
|
||||
var text = state === 'done'
|
||||
? button.getAttribute('data-signin-done-label') || '已签到'
|
||||
: (state === 'busy' ? '…' : button.getAttribute('data-signin-idle-label') || '签到');
|
||||
if (label) label.textContent = text;
|
||||
button.disabled = state !== 'idle';
|
||||
button.classList.toggle('is-done', state === 'done');
|
||||
});
|
||||
}
|
||||
|
||||
// 签到成功后,入口按钮就地替换为「已签到」详情弹窗触发链接
|
||||
// (参考主题:.initiate-checkin → details_link 的替换行为)。
|
||||
function swapButtonsToDetailsLink() {
|
||||
var url = window.aksrtData && window.aksrtData.signinDetailsUrl;
|
||||
if (!url) return;
|
||||
currentButtons().forEach(function (button) {
|
||||
var label = button.querySelector('[data-signin-label]');
|
||||
if (label) {
|
||||
label.textContent = button.getAttribute('data-signin-done-label') || '已签到';
|
||||
label.removeAttribute('data-signin-label');
|
||||
}
|
||||
var link = document.createElement('a');
|
||||
link.href = 'javascript:;';
|
||||
link.className = (button.className || '') + ' checkin-details-link';
|
||||
@@ -88,12 +82,17 @@
|
||||
link.setAttribute('data-aksrt-modal-mobile-bottom', 'true');
|
||||
link.setAttribute('data-aksrt-modal-cache', 'false');
|
||||
link.innerHTML = button.innerHTML;
|
||||
if (link.classList.contains('uc-header__signin-btn')) link.classList.add('is-done');
|
||||
button.replaceWith(link);
|
||||
});
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (signinBusy) return;
|
||||
var sequence = ++refreshSequence;
|
||||
api('/signin/today').then(function (result) {
|
||||
// A status read started before a click must not overwrite its result.
|
||||
if (signinBusy || sequence !== refreshSequence) return;
|
||||
if (!result.ok) {
|
||||
var message = (result.json && result.json.message) || '签到状态加载失败 (HTTP ' + result.status + ')';
|
||||
setStatus(message);
|
||||
@@ -104,17 +103,19 @@
|
||||
var data = result.json;
|
||||
if (!data.enabled) {
|
||||
setStatus('签到功能未启用');
|
||||
setButtonState('done');
|
||||
currentButtons().forEach(function (button) {
|
||||
button.disabled = true;
|
||||
button.setAttribute('disabled', '');
|
||||
button.setAttribute('aria-disabled', 'true');
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (data.signed) {
|
||||
setStatusHtml('已签到 <strong>' + data.streak + '</strong> 天 +' + data.reward_today);
|
||||
setButtonState('done');
|
||||
swapButtonsToDetailsLink();
|
||||
return;
|
||||
}
|
||||
setStatusHtml('连续 <strong>' + data.streak + '</strong> 天,+' + data.reward_if_signin);
|
||||
setButtonState('idle');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -122,62 +123,59 @@
|
||||
return document.querySelector('[data-signin-captcha]');
|
||||
}
|
||||
|
||||
function doSignin(captchaPayload) {
|
||||
notyf('正在签到…', 'load', '', 'aksrt-balance-signin-toast');
|
||||
api('/signin', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ captcha: captchaPayload || {} })
|
||||
}).then(function (result) {
|
||||
signinBusy = false;
|
||||
if (!result.ok) {
|
||||
notyf((result.json && result.json.message) || '签到失败', 'danger', 5, 'aksrt-balance-signin-toast');
|
||||
setButtonState('idle');
|
||||
return;
|
||||
}
|
||||
|
||||
notyf(result.json.message || '签到成功', 'success', 5, 'aksrt-balance-signin-toast');
|
||||
document.querySelectorAll('[data-credits-value]').forEach(function (element) {
|
||||
element.textContent = Number(result.json.balance || 0).toLocaleString();
|
||||
element.title = element.textContent;
|
||||
});
|
||||
setButtonState('done');
|
||||
swapButtonsToDetailsLink();
|
||||
refresh();
|
||||
}).finally(function () {
|
||||
if (window.AKSRTCaptcha) window.AKSRTCaptcha.reset('signin', captchaMount());
|
||||
});
|
||||
}
|
||||
|
||||
freshButtons.forEach(function (button) {
|
||||
button.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
if (signinBusy || button.disabled) return;
|
||||
|
||||
signinBusy = true;
|
||||
setButtonState('busy');
|
||||
++refreshSequence;
|
||||
var originalHtml = button.innerHTML;
|
||||
button.disabled = true;
|
||||
button.setAttribute('disabled', '');
|
||||
button.setAttribute('aria-busy', 'true');
|
||||
button.innerHTML = '<i class="loading mr6" aria-hidden="true"></i><text>请稍后</text>';
|
||||
|
||||
function restoreButton() {
|
||||
button.innerHTML = originalHtml;
|
||||
button.disabled = false;
|
||||
button.removeAttribute('disabled');
|
||||
button.removeAttribute('aria-busy');
|
||||
}
|
||||
|
||||
var captchaRequired = !!window.aksrtData.captchaSignin;
|
||||
var captcha = window.AKSRTCaptcha;
|
||||
var mount = captchaMount();
|
||||
|
||||
if (captchaRequired && (!captcha || !mount)) {
|
||||
signinBusy = false;
|
||||
notyf('人机验证未正确加载,请刷新页面后重试', 'danger');
|
||||
setButtonState('idle');
|
||||
return;
|
||||
}
|
||||
if (!captchaRequired) {
|
||||
doSignin({});
|
||||
return;
|
||||
}
|
||||
|
||||
captcha.run('signin', mount).then(function (payload) {
|
||||
Promise.resolve().then(function () {
|
||||
if (captchaRequired && (!captcha || !mount)) {
|
||||
throw new Error('人机验证未正确加载,请刷新页面后重试');
|
||||
}
|
||||
return captchaRequired ? captcha.run('signin', mount) : {};
|
||||
}).then(function (payload) {
|
||||
if (payload === null) throw new Error('已取消验证');
|
||||
doSignin(payload);
|
||||
notyf('正在签到,请稍后...', 'load', '', 'aksrt-balance-signin-toast');
|
||||
return api('/signin', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ captcha: payload })
|
||||
});
|
||||
}).then(function (result) {
|
||||
if (!result.ok) throw new Error(result.json.message || '签到失败');
|
||||
|
||||
restoreButton();
|
||||
notyf(result.json.message || '签到成功', 'success', 5, 'aksrt-balance-signin-toast');
|
||||
document.querySelectorAll('[data-credits-value]').forEach(function (element) {
|
||||
element.textContent = Number(result.json.balance || 0).toLocaleString();
|
||||
element.title = element.textContent;
|
||||
});
|
||||
setStatusHtml('已签到 <strong>' + result.json.streak + '</strong> 天 +' + result.json.reward);
|
||||
swapButtonsToDetailsLink();
|
||||
}).catch(function (error) {
|
||||
restoreButton();
|
||||
notyf((error && error.message) || '签到失败,请稍后重试', 'danger', 5, 'aksrt-balance-signin-toast');
|
||||
}).finally(function () {
|
||||
signinBusy = false;
|
||||
captcha.reset('signin', mount);
|
||||
notyf((error && error.message) || '人机验证加载中,请稍候再试', 'danger');
|
||||
setButtonState('idle');
|
||||
if (captcha && mount) captcha.reset('signin', mount);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ if (!function_exists('aksrt_header_user_checkin_btn')) :
|
||||
);
|
||||
}
|
||||
return aksrt_get_user_checkin_details_link(
|
||||
'but block c-yellow is-done',
|
||||
'but block c-yellow',
|
||||
'<i class="fa far fa-calendar-check" aria-hidden="true"></i> '
|
||||
. esc_html__('今日已签到', 'aksrt')
|
||||
);
|
||||
@@ -75,7 +75,6 @@ if (!function_exists('aksrt_header_user_checkin_btn')) :
|
||||
// instead of the UA button stylesheet (font/UI 1:1 with the reference).
|
||||
if ($class !== null) {
|
||||
return '<a class="' . esc_attr($class) . '" href="javascript:;" data-signin-btn'
|
||||
. ' data-signin-idle-label="' . esc_attr__('签到', 'aksrt') . '"'
|
||||
. ' data-signin-done-label="' . esc_attr__('已签到', 'aksrt') . '">'
|
||||
. '<i class="fa far fa-calendar-check ml3 mr6" aria-hidden="true"></i>'
|
||||
. '<span data-signin-label>' . esc_html__('签到', 'aksrt') . '</span>'
|
||||
@@ -83,7 +82,6 @@ if (!function_exists('aksrt_header_user_checkin_btn')) :
|
||||
}
|
||||
|
||||
return '<a class="but block c-yellow" href="javascript:;" data-signin-btn'
|
||||
. ' data-signin-idle-label="' . esc_attr__('签到', 'aksrt') . '"'
|
||||
. ' data-signin-done-label="' . esc_attr__('今日已签到', 'aksrt') . '">'
|
||||
. '<i class="fa far fa-calendar-check" aria-hidden="true"></i> '
|
||||
. '<span data-signin-label>' . esc_html__('签到领取今日奖励', 'aksrt') . '</span>'
|
||||
|
||||
@@ -35,7 +35,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
|
||||
<?php if (!empty($signin_signed_today)) : ?>
|
||||
<?php echo aksrt_get_user_checkin_details_link('uc-asset-card__signin muted-color px12', '<span>' . esc_html__('已签到', 'aksrt') . '</span><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i>'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php else : ?>
|
||||
<button type="button" class="uc-asset-card__signin muted-color px12" data-signin-btn data-signin-idle-label="<?php esc_attr_e('签到领积分', 'aksrt'); ?>" data-signin-done-label="<?php esc_attr_e('已签到', 'aksrt'); ?>" disabled>
|
||||
<button type="button" class="uc-asset-card__signin muted-color px12" data-signin-btn data-signin-done-label="<?php esc_attr_e('已签到', 'aksrt'); ?>">
|
||||
<span data-signin-label><?php esc_html_e('签到领积分', 'aksrt'); ?></span><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -85,7 +85,6 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
|
||||
type="button"
|
||||
class="btn btn-sm uc-header__signin-btn"
|
||||
data-signin-btn
|
||||
data-signin-idle-label="<?php esc_attr_e('签到', 'aksrt'); ?>"
|
||||
data-signin-done-label="<?php esc_attr_e('已签到', 'aksrt'); ?>"
|
||||
>
|
||||
<i class="fa fas fa-calendar-check" aria-hidden="true"></i>
|
||||
|
||||
@@ -338,7 +338,6 @@ class Signin {
|
||||
</div>';
|
||||
} else {
|
||||
$checkin_btn = '<button type="button" class="but c-blue ml10 p2-10 radius" data-signin-btn'
|
||||
. ' data-signin-idle-label="' . esc_attr__('立即签到', 'aksrt') . '"'
|
||||
. ' data-signin-done-label="' . esc_attr__('今日已签到', 'aksrt') . '">'
|
||||
. '<span data-signin-label>' . esc_html__('立即签到', 'aksrt') . '</span></button>';
|
||||
}
|
||||
|
||||
@@ -115,11 +115,13 @@ test('sign-in waits for main before binding and invokes the signin captcha', asy
|
||||
const button = {
|
||||
dataset: {},
|
||||
disabled: false,
|
||||
attrs: {},
|
||||
classList: { toggle() {} },
|
||||
setAttribute(name, value) { this.attrs[name] = value; },
|
||||
removeAttribute(name) { delete this.attrs[name]; },
|
||||
addEventListener(name, handler) { listeners[name] = handler; },
|
||||
querySelector(selector) { return selector === '[data-signin-label]' ? label : null; },
|
||||
getAttribute(name) {
|
||||
if (name === 'data-signin-idle-label') return '签到';
|
||||
if (name === 'data-signin-done-label') return '已签到';
|
||||
return '';
|
||||
},
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
const source = fs.readFileSync(path.join(root, 'assets/js/signin.js'), 'utf8');
|
||||
|
||||
class Element {
|
||||
constructor(tag, html = '', className = '', labels = {}) {
|
||||
this.tagName = tag.toUpperCase();
|
||||
this._html = html;
|
||||
this.className = className;
|
||||
this.dataset = {};
|
||||
this.attrs = {};
|
||||
this.listeners = {};
|
||||
this.replaced = null;
|
||||
this.disabled = false;
|
||||
this.title = '';
|
||||
this._label = null;
|
||||
this.labels = labels;
|
||||
this.classList = {
|
||||
contains: (name) => this.className.split(/\s+/).includes(name),
|
||||
add: (name) => { if (!this.classList.contains(name)) this.className = (this.className + ' ' + name).trim(); },
|
||||
};
|
||||
this.parseLabel();
|
||||
}
|
||||
get innerHTML() { return this._html; }
|
||||
set innerHTML(value) { this._html = value; this.parseLabel(); }
|
||||
parseLabel() {
|
||||
const match = this._html.match(/<span data-signin-label>(.*?)<\/span>/);
|
||||
if (!match) { this._label = null; return; }
|
||||
const parent = this;
|
||||
let value = match[1];
|
||||
let hasHook = true;
|
||||
this._label = {
|
||||
get textContent() { return value; },
|
||||
set textContent(text) { value = text; parent._html = parent._html.replace(/<span data-signin-label>.*?<\/span>/, '<span' + (hasHook ? ' data-signin-label' : '') + '>' + text + '</span>'); },
|
||||
removeAttribute(name) { if (name === 'data-signin-label') { hasHook = false; parent._html = parent._html.replace(' data-signin-label', ''); } },
|
||||
};
|
||||
}
|
||||
querySelector(selector) { return selector === '[data-signin-label]' ? this._label : null; }
|
||||
addEventListener(name, callback) { (this.listeners[name] ||= []).push(callback); }
|
||||
getAttribute(name) { return this.attrs[name] ?? this.labels[name] ?? ''; }
|
||||
setAttribute(name, value) { this.attrs[name] = String(value); }
|
||||
removeAttribute(name) { delete this.attrs[name]; }
|
||||
replaceWith(node) { this.replaced = node; }
|
||||
click() { (this.listeners.click || []).forEach((callback) => callback({ preventDefault() {} })); }
|
||||
}
|
||||
|
||||
function makeButton(className, label, icon = '<i class="fa fa-calendar-check"></i>', doneLabel = '已签到') {
|
||||
return new Element('button', icon + '<span data-signin-label>' + label + '</span>', className, {
|
||||
'data-signin-done-label': doneLabel,
|
||||
});
|
||||
}
|
||||
|
||||
function deferred() {
|
||||
let resolve;
|
||||
let reject;
|
||||
const promise = new Promise((yes, no) => { resolve = yes; reject = no; });
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
function setup({ buttons = [makeButton('btn uc-header__signin-btn', '签到')], captchaSignin = false, today = Promise.resolve({ ok: true, status: 200, json: { enabled: true, signed: false, streak: 1, reward_if_signin: 2 } }), signin = Promise.resolve({ ok: true, status: 200, json: { message: '签到成功,积分 +2', balance: 12 } }), captchaRun } = {}) {
|
||||
const calls = [];
|
||||
const notices = [];
|
||||
const balance = { textContent: '10', title: '10' };
|
||||
const status = { innerHTML: '', textContent: '' };
|
||||
const modals = [];
|
||||
const listeners = {};
|
||||
const mount = { nodeType: 1 };
|
||||
const doc = {
|
||||
querySelectorAll(selector) {
|
||||
if (selector === '[data-signin-btn]') return buttons.filter((button) => !button.replaced);
|
||||
if (selector === '[data-credits-value]') return [balance];
|
||||
if (selector === '[data-signin-status]') return [status];
|
||||
return [];
|
||||
},
|
||||
querySelector(selector) { return selector === '[data-signin-captcha]' ? mount : null; },
|
||||
createElement(tag) { return new Element(tag); },
|
||||
};
|
||||
const jq = { off() { return this; }, on(event, callback) { listeners[event] = callback; return this; } };
|
||||
const window = {
|
||||
aksrtData: { captchaSignin, signinDetailsUrl: '/wp-json/aksrt/v1/signin/details' },
|
||||
AksrtModalRuntime: { open: (...args) => modals.push(args), openRemote: (...args) => modals.push(args) },
|
||||
AksrtRequest: { request(endpoint, options) { calls.push({ endpoint, options }); return endpoint === '/signin/today' ? today : signin; } },
|
||||
};
|
||||
const captchaCalls = [];
|
||||
if (captchaRun) window.AKSRTCaptcha = {
|
||||
run(scene, scope) { captchaCalls.push(['run', scene, scope]); return captchaRun; },
|
||||
reset(scene, scope) { captchaCalls.push(['reset', scene, scope]); },
|
||||
};
|
||||
vm.runInNewContext(source, { window, document: doc, jQuery: () => jq, notyf: (...args) => notices.push(args), Promise, JSON, Number, tbquire: (_deps, ready) => { ready(); } });
|
||||
return { buttons, calls, notices, listeners, window, captchaCalls, mount, balance, status, modals };
|
||||
}
|
||||
|
||||
const settle = () => new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
test('uses reference loading, success toast, and replaces all entry points with detail links', async () => {
|
||||
const first = makeButton('img-badge jb-yellow', '签到', '<i class="fa far fa-calendar-check ml3 mr6"></i>');
|
||||
const second = makeButton('btn uc-header__signin-btn', '签到', '<i class="fa fa-calendar-check"></i>', '今日已签到');
|
||||
const success = deferred();
|
||||
const h = setup({ buttons: [first, second], signin: success.promise });
|
||||
await settle();
|
||||
const originalOther = second.innerHTML;
|
||||
first.click();
|
||||
assert.equal(first.disabled, true);
|
||||
assert.equal(first.innerHTML, '<i class="loading mr6" aria-hidden="true"></i><text>请稍后</text>');
|
||||
assert.equal(first.getAttribute('aria-busy'), 'true');
|
||||
assert.ok(Object.hasOwn(first.attrs, 'disabled'));
|
||||
assert.equal(second.innerHTML, originalOther, 'non-clicked entries keep their content while the request runs');
|
||||
assert.equal(second.disabled, false);
|
||||
first.click();
|
||||
second.click();
|
||||
await settle();
|
||||
assert.equal(h.calls.filter((call) => call.endpoint === '/signin').length, 1);
|
||||
success.resolve({ ok: true, status: 200, json: { message: '签到成功,积分 +2', balance: 12, streak: 2, reward: 2 } });
|
||||
await settle();
|
||||
assert.deepEqual(h.notices.find((notice) => notice[1] === 'success')?.slice(0, 2), ['签到成功,积分 +2', 'success']);
|
||||
for (const original of [first, second]) {
|
||||
assert.equal(original.replaced?.getAttribute('data-aksrt-modal'), 'remote');
|
||||
assert.equal(original.replaced?.getAttribute('data-aksrt-modal-url'), '/wp-json/aksrt/v1/signin/details');
|
||||
assert.match(original.replaced.innerHTML, /已签到/);
|
||||
assert.doesNotMatch(original.replaced.innerHTML, /data-signin-label/);
|
||||
assert.match(original.replaced.className, /checkin-details-link/);
|
||||
assert.match(original.replaced.innerHTML, /fa-calendar-check/);
|
||||
assert.equal(Object.hasOwn(original.replaced.attrs, 'disabled'), false);
|
||||
assert.equal(original.replaced.getAttribute('aria-busy'), '');
|
||||
}
|
||||
assert.doesNotMatch(first.replaced.className, /is-done/);
|
||||
assert.match(second.replaced.innerHTML, /今日已签到/);
|
||||
assert.equal(second.replaced.className.includes('uc-header__signin-btn'), true);
|
||||
assert.equal(h.calls.filter((call) => call.endpoint === '/signin/today').length, 1, 'success does not issue another status request');
|
||||
assert.equal(h.modals.length, 0, 'success does not open a modal');
|
||||
assert.equal(h.balance.textContent, '12');
|
||||
assert.equal(h.status.innerHTML, '已签到 <strong>2</strong> 天 +2');
|
||||
});
|
||||
|
||||
test('keeps the profile done style on its replacement link', async () => {
|
||||
const button = makeButton('btn uc-header__signin-btn', '签到');
|
||||
const success = deferred();
|
||||
const h = setup({ buttons: [button], signin: success.promise });
|
||||
await settle();
|
||||
button.click();
|
||||
success.resolve({ ok: true, status: 200, json: { message: '签到成功', balance: 1 } });
|
||||
await settle();
|
||||
assert.match(button.replaced.className, /is-done/);
|
||||
});
|
||||
|
||||
test('restores complete button markup after a rejected sign-in response', async () => {
|
||||
const button = makeButton('img-badge jb-yellow', '签到', '<i class="fa far fa-calendar-check ml3 mr6"></i>');
|
||||
const initial = button.innerHTML;
|
||||
const h = setup({ buttons: [button], signin: Promise.resolve({ ok: false, status: 500, json: { message: '签到失败' } }) });
|
||||
await settle();
|
||||
button.click();
|
||||
await settle();
|
||||
assert.equal(button.innerHTML, initial);
|
||||
assert.equal(button.disabled, false);
|
||||
assert.equal(button.getAttribute('aria-busy'), '');
|
||||
assert.equal(button.replaced, null);
|
||||
});
|
||||
|
||||
test('preserves the existing main/captcha ordering and resets captcha after submission', async () => {
|
||||
const button = makeButton('btn', '签到');
|
||||
const success = deferred();
|
||||
const captcha = deferred();
|
||||
const h = setup({ buttons: [button], captchaSignin: true, signin: success.promise, captchaRun: captcha.promise });
|
||||
await settle();
|
||||
button.click();
|
||||
await settle();
|
||||
assert.deepEqual(h.captchaCalls[0]?.slice(0, 2), ['run', 'signin']);
|
||||
assert.equal(h.calls.some((call) => call.endpoint === '/signin'), false);
|
||||
captcha.resolve({ proof: 'ok' });
|
||||
await settle();
|
||||
assert.equal(h.calls.filter((call) => call.endpoint === '/signin').length, 1);
|
||||
success.resolve({ ok: true, status: 200, json: { message: '签到成功', balance: 1 } });
|
||||
await settle();
|
||||
assert.deepEqual(h.captchaCalls.map((call) => call[0]), ['run', 'reset']);
|
||||
});
|
||||
|
||||
test('allows retry after a rejected request', async () => {
|
||||
const button = makeButton('btn', '签到');
|
||||
const today = deferred();
|
||||
const h = setup({ buttons: [button], today: today.promise, signin: Promise.reject(Object.assign(new Error('network down'), { status: 0 })) });
|
||||
button.click();
|
||||
await settle();
|
||||
today.resolve({ ok: true, status: 200, json: { enabled: true, signed: false, streak: 2, reward_if_signin: 3 } });
|
||||
await settle();
|
||||
assert.equal(button.innerHTML.includes('请稍后'), false);
|
||||
assert.equal(button.disabled, false);
|
||||
assert.equal(button.replaced, null);
|
||||
button.click();
|
||||
await settle();
|
||||
assert.equal(h.calls.filter((call) => call.endpoint === '/signin').length, 2);
|
||||
});
|
||||
|
||||
test('keeps the loading button when a previous status read completes during submission', async () => {
|
||||
const button = makeButton('img-badge jb-yellow', '签到');
|
||||
const today = deferred();
|
||||
const signin = deferred();
|
||||
const h = setup({ buttons: [button], today: today.promise, signin: signin.promise });
|
||||
button.click();
|
||||
await settle();
|
||||
today.resolve({ ok: true, status: 200, json: { enabled: true, signed: true, streak: 2, reward_today: 3 } });
|
||||
await settle();
|
||||
assert.equal(button.replaced, null);
|
||||
assert.match(button.innerHTML, /loading mr6/);
|
||||
assert.equal(button.disabled, true);
|
||||
assert.equal(h.status.innerHTML, '');
|
||||
signin.resolve({ ok: true, status: 200, json: { message: '签到成功', balance: 1, streak: 2, reward: 3 } });
|
||||
await settle();
|
||||
assert.ok(button.replaced);
|
||||
});
|
||||
|
||||
test('ignores an old today response after successful sign-in', async () => {
|
||||
const button = makeButton('btn', '签到');
|
||||
const today = deferred();
|
||||
const signin = deferred();
|
||||
const h = setup({ buttons: [button], today: today.promise, signin: signin.promise });
|
||||
button.click();
|
||||
await settle();
|
||||
signin.resolve({ ok: true, status: 200, json: { message: '签到成功', balance: 1, streak: 3, reward: 2 } });
|
||||
await settle();
|
||||
today.resolve({ ok: true, status: 200, json: { enabled: true, signed: false, streak: 2, reward_if_signin: 3 } });
|
||||
await settle();
|
||||
assert.ok(button.replaced, 'success replaced the action with the details link');
|
||||
assert.match(button.replaced.innerHTML, /已签到/);
|
||||
assert.equal(h.status.innerHTML, '已签到 <strong>3</strong> 天 +2');
|
||||
});
|
||||
|
||||
test('leaves a disabled sign-in feature disabled without turning it into a signed state', async () => {
|
||||
const button = makeButton('btn', '签到');
|
||||
const h = setup({ buttons: [button], today: Promise.resolve({ ok: true, status: 200, json: { enabled: false, signed: false } }) });
|
||||
await settle();
|
||||
assert.equal(button.disabled, true);
|
||||
assert.equal(button.replaced, null);
|
||||
assert.doesNotMatch(button.className, /is-done/);
|
||||
assert.equal(button.innerHTML.includes('已签到'), false);
|
||||
});
|
||||
|
||||
test('does not send the request when captcha is absent or cancelled and keeps original markup', async () => {
|
||||
const missingButton = makeButton('btn', '签到');
|
||||
const missing = setup({ buttons: [missingButton], captchaSignin: true });
|
||||
missing.window.AKSRTCaptcha = undefined;
|
||||
// No mount is needed in the harness to verify the missing provider path.
|
||||
missingButton.click();
|
||||
await settle();
|
||||
assert.equal(missingButton.disabled, false);
|
||||
assert.equal(missing.calls.some((call) => call.endpoint === '/signin'), false);
|
||||
|
||||
const cancelledButton = makeButton('btn', '签到');
|
||||
const cancelled = setup({ buttons: [cancelledButton], captchaSignin: true, captchaRun: Promise.resolve(null) });
|
||||
const original = cancelledButton.innerHTML;
|
||||
cancelledButton.click();
|
||||
await settle();
|
||||
assert.equal(cancelledButton.innerHTML, original);
|
||||
assert.equal(cancelledButton.disabled, false);
|
||||
assert.equal(cancelled.calls.some((call) => call.endpoint === '/signin'), false);
|
||||
});
|
||||
|
||||
test('does not bind the same button twice through repeated auto_fun scans', async () => {
|
||||
const button = makeButton('btn', '签到');
|
||||
const h = setup({ buttons: [button] });
|
||||
await settle();
|
||||
h.listeners['auto_fun.aksrt-signin']();
|
||||
assert.equal(button.listeners.click.length, 1);
|
||||
button.click();
|
||||
await settle();
|
||||
assert.equal(h.calls.filter((call) => call.endpoint === '/signin').length, 1);
|
||||
});
|
||||
Reference in new issue
Block a user