feat: integrate login flow, lobby UI and bitmap font resources
This commit is contained in:
@@ -3,9 +3,32 @@ import assert from 'node:assert/strict';
|
||||
import { LoginAccounts, nicknameHash } from '../../YouleNexus/assets/framework/adapters/native/login-accounts.ts';
|
||||
import { resolveLoginPolicy } from '../../YouleNexus/assets/framework/presentation/login-policy.ts';
|
||||
import { buildLoginRequest } from '../../YouleNexus/assets/framework/protocol/contracts/login-contract.ts';
|
||||
|
||||
test('logout expires visitor auto-login and clears cached player selection',()=>{
|
||||
const values=new Map<string,string>();
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:k=>k==='showVisitor'?1:null}),submit:()=>{},tip:()=>{}});
|
||||
service.setAgreement(true);service.visitor();assert.ok(service.cached());
|
||||
service.logout();assert.equal(service.cached(),null);
|
||||
assert.equal(values.get('tsgame_playerid_ga'),'');
|
||||
});
|
||||
test('logout removes authorization caches and stays logged out after recreating accounts',()=>{
|
||||
for(const mode of ['wechat','phone','visitor']){
|
||||
const account=JSON.stringify({openid:'old',headimgurl:'a',nickname:'old',sex:1,city:'c',Province:'p',unionid:'old',telphone:'123'});
|
||||
const values=new Map([['tsgame_wxinfo',account],['tsgame_devicePlayer',account],['tsgame_visitorinfo',account],
|
||||
['tsgame_validtime','100000'],['tsgame_playerid_ga','42'],['tsgame_dLogin','1'],['tsgame_machineId_','installation']]);
|
||||
const options={storage:{getItem:(k:string)=>values.get(k)??null,setItem:(k:string,v:string)=>{values.set(k,v);},removeItem:(k:string)=>{values.delete(k);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor:mode==='visitor',deviceLogin:mode==='phone'},submit:()=>{},tip:()=>{}};
|
||||
const service=new LoginAccounts(options);assert.ok(service.cached());service.logout();service.logout();
|
||||
assert.equal(values.has('tsgame_wxinfo'),false);assert.equal(values.has('tsgame_devicePlayer'),false);
|
||||
assert.equal(values.get('tsgame_validtime'),'0');assert.equal(values.get('tsgame_dLogin'),'0');assert.equal(values.get('tsgame_playerid_ga'),'');
|
||||
assert.equal(new LoginAccounts(options).cached(),null);
|
||||
assert.equal(values.get('tsgame_visitorinfo'),account);assert.equal(values.get('tsgame_machineId_'),'installation');
|
||||
}
|
||||
});
|
||||
test('native authorization preserves provider numbers, agreement guard, callback and cache', () => {
|
||||
const values=new Map<string,string>();const calls:number[]=[];const submitted:any[]=[];const tips:string[]=[];
|
||||
const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
settings:{accreditlogin:p=>calls.push(p),getcompareCode:()=>0},random:()=>0.2,now:()=>100,
|
||||
gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:()=>null}),submit:(a,d)=>submitted.push([a,d]),tip:t=>tips.push(t)});
|
||||
service.setAgreement(false);service.authorize('wechat');assert.deepEqual(calls,[]);assert.deepEqual(tips,['未同意协议']);
|
||||
@@ -16,7 +39,7 @@ test('native authorization preserves provider numbers, agreement guard, callback
|
||||
});
|
||||
test('all login paths require the current agreement and rejected paths have no side effects',()=>{
|
||||
const values=new Map<string,string>(),tips:string[]=[],submitted:unknown[]=[],native:number[]=[];
|
||||
const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
settings:{getcompareCode:()=>0,accreditlogin:p=>native.push(p)},random:()=>0.2,now:()=>100,
|
||||
gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:()=>null}),submit:a=>submitted.push(a),tip:t=>tips.push(t)});
|
||||
assert.equal(service.agreed,true);service.setAgreement(false);
|
||||
@@ -29,7 +52,7 @@ test('all login paths require the current agreement and rejected paths have no s
|
||||
});
|
||||
test('successful device login persists server identity for the next automatic login', () => {
|
||||
const values=new Map<string,string>();const submitted:any[]=[];
|
||||
const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),deviceLogin:true},
|
||||
submit:(a,d)=>submitted.push([a,d]),tip:()=>{}});
|
||||
service.loginSucceeded({state:0,playerid:42,openid:'server-o',avatar:'server-a',nickname:'server-n',sex:2,city:'c',province:'p',unionid:'u',tel:'13800138000'}, {enabled:true,telphone:'13800138000'});
|
||||
@@ -39,21 +62,40 @@ test('successful device login persists server identity for the next automatic lo
|
||||
});
|
||||
test('visitor login and cached visitor stay phone-free when both login buttons are enabled', () => {
|
||||
const values=new Map<string,string>();const submitted:any[]=[];
|
||||
const options={storage:{getItem:(k:string)=>values.get(k)??null,setItem:(k:string,v:string)=>{values.set(k,v);}},
|
||||
const options={storage:{removeItem:(k:string)=>{values.delete(k);},getItem:(k:string)=>values.get(k)??null,setItem:(k:string,v:string)=>{values.set(k,v);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor:true,deviceLogin:true},
|
||||
submit:(a:unknown,d:unknown)=>submitted.push([a,d]),tip:()=>{}};
|
||||
const service=new LoginAccounts(options);
|
||||
service.visitor();assert.deepEqual(submitted[0][1],{enabled:false});
|
||||
assert.doesNotThrow(()=>service.loginSucceeded({state:0,playerid:42},submitted[0][1]));
|
||||
const response={state:0,playerid:42,openid:'server',avatar:'a',nickname:'n',sex:0,city:'c',province:'p',unionid:'server'};
|
||||
assert.doesNotThrow(()=>service.loginSucceeded(response,submitted[0][1]));
|
||||
assert.equal(values.get('tsgame_playerid_ga'),'42');
|
||||
assert.equal(values.has('tsgame_devicePlayer'),false);assert.equal(values.has('tsgame_dLogin'),false);
|
||||
assert.equal(JSON.parse(values.get('tsgame_devicePlayer')!).sex,0);assert.equal(values.get('tsgame_dLogin'),'1');
|
||||
const reloaded=new LoginAccounts(options);reloaded.autoLogin();
|
||||
assert.deepEqual(submitted[1],[submitted[0][0],{enabled:false}]);
|
||||
assert.doesNotThrow(()=>reloaded.loginSucceeded({state:0,playerid:42},submitted[1][1]));
|
||||
assert.doesNotThrow(()=>reloaded.loginSucceeded(response,submitted[1][1]));
|
||||
});
|
||||
|
||||
test('phone cache writes match legacy before submit, denial, and successful server response',()=>{
|
||||
for(const showVisitor of [false,true])for(const deviceLogin of [false,true]){
|
||||
const values=new Map<string,string>();let now=100;let submitted:any;
|
||||
const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);},removeItem:k=>{values.delete(k);}},
|
||||
random:()=>0.2,now:()=>now,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor,deviceLogin},
|
||||
submit:(account,mode)=>{submitted={account,mode};assert.equal(values.has('tsgame_visitorinfo'),showVisitor);assert.equal(values.has('tsgame_devicePlayer'),false);},tip:()=>{}});
|
||||
service.phone('00123','000456');
|
||||
const before=[...values];assert.throws(()=>service.loginSucceeded({state:-1,msg:'短信验证码不正确'},submitted.mode));assert.deepEqual([...values],before);
|
||||
now=200;
|
||||
service.loginSucceeded({state:0,playerid:42,openid:'server',avatar:'avatar',nickname:'server name',sex:0,city:'city',province:'province',unionid:'server union',tel:'00123'},submitted.mode);
|
||||
assert.equal(values.has('tsgame_devicePlayer'),deviceLogin);
|
||||
if(deviceLogin){assert.deepEqual(JSON.parse(values.get('tsgame_devicePlayer')!),{openid:'server',headimgurl:'avatar',nickname:'server name',sex:0,city:'city',Province:'province',unionid:'server union',telphone:'00123'});assert.equal(values.get('tsgame_validtime'),String(200+48*60*60*1000));assert.equal(values.get('tsgame_dLogin'),'1');}
|
||||
if(showVisitor)assert.equal(JSON.parse(values.get('tsgame_visitorinfo')!).openid,'phone_00123');
|
||||
assert.equal(values.get('tsgame_playerid_ga'),'42');
|
||||
assert.equal([...values.values()].some(v=>v.includes('000456')),false,'SMS code is not persisted');
|
||||
}
|
||||
});
|
||||
test('global and WVJB authorization retain their distinct legacy cache destinations', () => {
|
||||
const values=new Map<string,string>();
|
||||
const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor:true},submit:()=>{},tip:()=>{}});
|
||||
const data={openid:'o',headimgurl:'a',nickname:'n',sex:1,city:'c',Province:'p',unionid:'u'};
|
||||
service.acceptNative(data,'global');assert.ok(values.has('tsgame_visitorinfo'));assert.equal(values.has('tsgame_wxinfo'),false);
|
||||
@@ -63,9 +105,22 @@ test('legacy nickname MD5 matches standard vectors',()=>{
|
||||
assert.equal(nicknameHash(''),'d41d8cd98f00b204e9800998ecf8427e');
|
||||
assert.equal(nicknameHash('abc'),'900150983cd24fb0d6963f7d28e17f72');
|
||||
});
|
||||
test('phone login preserves leading zeros and legacy visitor cache before submitting', () => {
|
||||
const values=new Map<string,string>();const tips:string[]=[];const submissions:unknown[]=[];
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor:true},
|
||||
submit:(account,mode)=>{assert.equal(JSON.parse(values.get('tsgame_visitorinfo')!).telphone,'00123');submissions.push([account,mode]);},tip:m=>tips.push(m)});
|
||||
service.phone('','000456');service.phone('00123','');
|
||||
assert.equal(submissions.length,0);assert.equal(values.size,0);
|
||||
assert.deepEqual(tips,['手机号与验证码均不能为空!','手机号与验证码均不能为空!']);
|
||||
service.phone('00123','000456');
|
||||
assert.deepEqual((submissions[0] as unknown[])[1],{enabled:true,telphone:'00123',smmcode:'000456'});
|
||||
assert.equal(values.has('tsgame_devicePlayer'),false);
|
||||
});
|
||||
|
||||
test('phone login emits original sms fields and does not masquerade as cached auto login', () => {
|
||||
const values=new Map<string,string>();const submitted:any[]=[];
|
||||
const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
const service=new LoginAccounts({storage:{removeItem:(k:string)=>{values.delete(k);},getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}},
|
||||
random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:()=>null}),
|
||||
submit:(a,d)=>submitted.push([a,d]),tip:()=>{}});
|
||||
service.phone('13800138000','123456');
|
||||
|
||||
Reference in New Issue
Block a user